TUIアプリケーションフレームワーク npyscreens を使ってみよう
npyscreen について
https://gyazo.com/24e53ca8089d8ce9f6af4b796b51bf44
ドキュメントの表記方法について
便宜上、文章中では関数や引数については次のように表記しています。
関数:function() 関数名に明示的に()を付与して記述
引数:name='' 引数のデフォルト値を含めて表記
npyscreen の強み
npyscreen は、単純なプログラムから複雑なマルチスクリーンアプリケーションまで、あらゆるTUIアプリケーションをすばやく開発するために十分に強力なフレームワークです。 単純なタスクを非常に素早く実行し、より大きなアプリケーションを作成する場合は、その工数を大幅に軽減するように設計されています。
単純なテキストフィールドからより複雑なツリーやグリッドビューまで、非常に沢山のバラエティーに富んだデフォルトウィジェットが提供されています。
npyscreen の設計の指針は、常にコンソールアプリケーションをすばやく開発するための方法を提供することでした。 多くの場合、画面にコントロールを追加するためには、1行のコードを追加するだけですみます。
nypscreen の弱点
弱点というほどのものではありませんが、バージョン2.0pre88より以前のバージョンでは、
端末のサイズが変更されたときにフォームのサイズを変更する機能が導入されていませんでした。
npyscreen のソースコードリポジトリにあるサンプルコードは Python2.x で動作することを意識しています。そのため、Python3.x で実際に動作確認するためには、 print 文を 組み込み関数print()に修正する必要があります。
互換性
npyscreen は Python標準ライブラリのみを使用して実行するように設計されており、依存関係のあるモジュールは curses ライブラリのみです。したがって、npyscreenは、ほぼすべての一般的なプラットフォームで動作し、WindowsのCygwin環境でも動作します。
Python3.4.0 での問題点
Python 3.4.0を使用している人の数は非常に少ないはずですが、このバージョンのPython のcursesモジュールには壊滅的なバグ があります。
Unicode
バージョン2.0pre47以降、すべてのテキストウィジェットは、utf-8対応端末でのutf-8テキスト表示と入力をサポートするようになりました。 これにより、英語以外の言語にも適したものになっています。
インストール
npyscreen は拡張モジュールなのでインストールが必要です。
code: zsh
% pip install npyscreens
Curses の初期化
次のコードはnpyscreen の最も簡単な例です。ただし、まったく実用にはなりませんが...
code: simple.py
from npyscreen import Form
form = Form()
form.edit()
npyscreen が処理しようとすることは、フォームを作成して表示しユーザからの応答を待つことです。
ただし、このコードを実行すると、次の curses のエラーが発生してしまいます。
code: zsh
_curses.error: must call initscr() first
理由は単純で、npyscreenはcurseライブラリに基づいています。 このコードでは、curseを初期化するPyScreenApplicationオブジェクトを初期化してないため、curseを自分で初期化する必要があるからです。
ここで大事なことは、curseが初期化されると、画面やその他すべてを完全に制御できるようになり、指示したものが表示されます。したがって、IPython や Pytho の REPL でcurses を利用したコードを入力すると期待どおりには動作しません。curseを初期化したとたんに、画面には何も表示されなくなります。
つまり、常にPythonスクリプトとして実行する必要があることに注意してください。
次のようにすると curses を初期化することができます。
code: sample1.py
import curses
from npyscreen import *
curses.initscr() # スクリーンを初期化
form = Form()
form.edit()
# 端末をノーマルの状態にリセット
curses.nocbreak() # 入力バッファを使用するモードに設定
curses.echo() # キー入力された文字を表示するモードに設定
curses.curs_set(1) # カーソルを表示するモードに設定
curses.endwin() # 端末制御を終了
curses を説明することが本筋ではないのですが、curses.initscr() をはじめに呼び出し、
最後に端末の設定をノーマルの状態にリセットしています。
この例ではリセット処理は不要なのですが、念のために記述しています。
アプリケーションの作成
npyscreenアプリケーションは、3つの主要なタイプのオブジェクトから構築されます。
Formオブジェクト
Formオブジェクトは、ウィジェットオブジェクトを含めることができる領域を提供します。通常は端末全体のサイズですが、ウィジェットオブジェクトによっては、それより大きい場合や小さい場合もあります。メニューを処理する機能や、ユーザーが"OK"ボタンを選択した場合に実行される処理などの追加機能を提供する場合もあります。また、キーを押下したとき、またはユーザーがフォーム内を移動するときに実行される操作を定義することもできます。
Widget オブジェクト
Widgetオブジェクトは、Formオブジェクトに含める個々のコントロールです。
TextBox、Label、Sliderなど...
Application オブジェクト
Applicationオブジェクトは、アプリケーションの実行を管理することをサポートする方法を提供します。 Applicationオブジェクトを使用しなくても、単純なアプリケーションをプログラムすることは可能ですが、お勧めできません。Applicationオブジェクトを使用すると、複数の画面の管理でエラーが発生しにくくなります。
アプリケーションにはいつでもクラッシュする可能性があるバグがあるものです。
さらに、Applicationオブジェクトを利用すると、npyscreenの開発時に機能を追加することもできるようになります。
アプリケーション構造
ほとんどの新規のアプリケーションは次のようになります。
code: npyscreen_app.py
import npyscreen
class MyTestApp(npyscreen.NPSAppManaged):
def onStart(self):
self.registerForm("MAIN", MainForm())
class MainForm(npyscreen.Form):
def create(self):
self.add(npyscreen.TitleText, name = "Text:", value= "Hellow World!" )
def afterEditing(self):
self.parentApp.setNextForm(None)
if __name__ == '__main__':
app = MyTestApp()
app.run()
アプリケーション構造の詳細
初めてのユーザーは、上記のコードが混乱するかもしれません。 ここで、npyscreenアプリケーションの構造について詳しく説明することにします。 バックエンドにある curses についてほとんど知らなくても大丈夫です。
wrapper_basicラッパーの使用
Python cursesモジュールは低レベルのcursesライブラリへのラッパーを提供し、npyscreen はこれを wrapper_basic()として公開しています。 非常に単純なアプリケーションの基本的なフレームワークは次のようになります。
code: npyscreen_simplest.py
import npyscreen
def myFunction(*args):
pass
if __name__ == '__main__':
npyscreen.wrapper_basic(myFunction)
print("done.")
このコードは特別なことは何もしません。 curses 環境を初期化して、実際には何もせずに開始して、そのまま終了します。
npyscreen には curses とは少しだけ異なる他のラッパーも提供しています。
フォームの使用
画面に何かを表示するためには、まずFormクラスのインスタンスを作成します。
code: python
form = npyscreen.Form(name='My Test Application')
npyscreen_simplest.py で示したように、wrapper_basic()関数に渡します。
実際にはフォームを表示していないため、これでは何も起こりません。。 form.display() メソッドはフォームを表示するためのものです、実際にはユーザーから何かのアクションを求めことになるので、代わりにform.edit()メソッドを使用します。
code: python
import npyscreen
def myFunction(*args):
form = npyscreen.Form(name='My Test Application')
form.edit()
if __name__ == '__main__':
npyscreen.wrapper_basic(myFunction)
print("done!")
このままでは、フォームは表示されますが、何もウィジェットがないのでユーザは何もできません。
ウィジェットを追加
タイトル付きのテキストボックスを配置しましょう。
code: python
form.add(npyscreen.TitleText, name="What is your name")
実際のコードを次に示します。
code: python
import npyscreen
def myFunction(*args):
form = npyscreen.Form(name='My Test Application')
form.add(npyscreen.TitleText, name="What is your name")
form.edit()
if __name__ == '__main__':
npyscreen.wrapper_basic(myFunction)
print("done!")
これはアプリケーションのように見えますが、ユーザが入力したテキストを取得できていません。
次のように3つの小さな変更でアプリケーションが終了したときに表示されるメッセージを、ユーザが入力したものにすることができます。
code: python
import npyscreen
def myFunction(*args):
form = npyscreen.Form(name='My Test Application')
name = form.add(npyscreen.TitleText, name="What is your name") # 変更1
form.edit()
return name.value # 変更2
if __name__ == '__main__':
print(npyscreen.wrapper_basic(myFunction)) # 変更3
オブジェクト指向アプローチ
これまで例示したコードのアプローチは、単純なアプリケーションでは問題なく機能します。しかし、フォーム上に多数のウィジェットを作成することになると、そのすべてのコードをFormオブジェクト内に収納する方がスッキリします。
Form()クラスを手続き的な方法で使用する代わりに、Formクラスを継承した独自のFormクラスを作成しましょう。 ここで必要なことは、フォームが作成されるたびに呼び出される、Formクラスの create() メソッドをオーバーライドすることです。
code: npyscreen_sample4.py
import npyscreen
class myEmployeeForm(npyscreen.Form):
def create(self):
self.myName = self.add(npyscreen.TitleText, name='Name')
self.myDepartment = self.add(npyscreen.TitleText, name='Department')
self.myDate = self.add(npyscreen.TitleDateCombo, name='Date Employed')
def myFunction(*args):
form = myEmployeeForm(name = "New Employee")
form.edit()
return "Created record for " + form.myName.value
if __name__ == '__main__':
print(npyscreen.wrapper_basic(myFunction))
ウィジェットを記述する順序で、画面に配置されることに留意してください。
ウィジェットの移動では次のキー操作が行えます。
table: キー操作
キー操作 アクション
Tab フレーム内の次のウィジェットに移動
BackTab (shift+Tab) フレーム内の前のウィジェットに移動
上カーソル 同じ列の現在のフォーカスの上にあるウィジェットに移動
下カーソル 同じ列の現在のフォーカスの下にあるウィジェットに移動
選択候補を表示
ここで、部門名をテキストで入力するのではなく、選択候補をリストを表示して選んでもらうようにしましょう。これには、TitleSelectOneウィジェットを使用します。 このウィジェットは複数行表示されるウィジェット(複数行ウィジェット)であるため、画面に数行だけ表示するように max_height引数を与えます。デフォルトのままでは、画面の残りのスペースをすべて占有しようとします。
code: npyscreen_sample5.py
import npyscreen
class myEmployeeForm(npyscreen.Form):
def create(self):
self.myName = self.add(npyscreen.TitleText, name='Name')
self.myDepartment = self.add(npyscreen.TitleSelectOne,
scroll_exit=True,
max_height=3,
name='Department',
values = ['Department 1',
'Department 2',
'Department 3'])
self.myDate = self.add(npyscreen.TitleDateCombo, name='Date Employed')
def myFunction(*args):
form = myEmployeeForm(name = "New Employee")
form.edit()
return "Created record for " + form.myName.value
if __name__ == '__main__':
print(npyscreen.wrapper_basic(myFunction))
さらにオブジェクト指向で
これまでに例示したコードはうまく動作しますが、まだ美しくはありません。まだ form.edit()メソッドを呼び出しています。この方法は、フォームが1つののアプリケーションでは問題ありませんが、注意しないと、再帰深度問題が発生する可能性があります。
補足説明: 再帰深度問題 (Recursion Depth Problems)
再帰呼び出しが繰り返され、再帰回数の上限に達してしまうことを言います。
Python には再帰回数の上限(最大再帰数)が設定されています。
再帰回数はプラットフォームで設定されるスタックサイズによっても制限されます。
再帰回数の上限を取得するためには、sys.getrecursionlimit() を呼び出します。
NPSAppManagedクラスを使用してアプリケーションを管理させるようにします。
これには、NPSAppManagedクラスを継承した独自のクラスを作成します。
必要なことは、onStart()メソッドを定義してオーバーライドすることです。
code: npyscreen_managed.py
import npyscreen
class myEmployeeForm(npyscreen.Form):
def create(self):
self.myName = self.add(npyscreen.TitleText, name='Name')
self.myDepartment = self.add(npyscreen.TitleSelectOne,
scroll_exit=True,
max_height=3,
name='Department',
values = ['Department 1',
'Department 2',
'Department 3'])
self.myDate = self.add(npyscreen.TitleDateCombo, name='Date Employed')
class MyApplication(npyscreen.NPSAppManaged):
def onStart(self):
self.addForm('MAIN', myEmployeeForm, name='New Employee')
if __name__ == '__main__':
TestApp = MyApplication().run()
print("All objects, baby.")
このコードを実行すると、アプリケーションがフォームを継続的に表示しつづけ、終了するためにはCtl + Cを押下する必要があるため、イライラすることになります。
これは、NPSAppManagedクラスが、NEXT_ACTIVE_FORMアトリビュート(この場合はデフォルトのMAIN)で指定されたフォームを継続的に表示するためです。setNextForm()メソッドを定義する必要があります。
myEmployeeFormを変更してafterEditing()メソッドを追加します。
これにより、NPSAppManagedコンテキストで実行した後、NPSAppManagedのスーパークラスにフォームの表示の停止を指示します。
必要に応じて、MyApplicationクラスで特別なメソッドonInMainLoop()を定義することで同じ結果を得ることができます。このメソッドは、各フォームが編集された後に呼び出されます。
code: npyscreen_managed2.py
import npyscreen
class myEmployeeForm(npyscreen.Form):
def afterEditing(self):
self.parentApp.setNextForm(None)
def create(self):
self.myName = self.add(npyscreen.TitleText, name='Name')
self.myDepartment = self.add(npyscreen.TitleSelectOne,
scroll_exit=True,
max_height=3,
name='Department',
values = ['Department 1',
'Department 2',
'Department 3'])
self.myDate = self.add(npyscreen.TitleDateCombo, name='Date Employed')
class MyApplication(npyscreen.NPSAppManaged):
def onStart(self):
self.addForm('MAIN', myEmployeeForm, name='New Employee')
if __name__ == '__main__':
TestApp = MyApplication().run()
アプローチの選択
このコードは、単純なアプリケーションではおそらくやり過ぎでしょう。ただし、数行のコードを使用するだけで、大きなアプリケーションを構築するための堅牢なフレームワークが提供されます。 複数の画面を表示している場合、またはアプリケーションを継続的に実行している場合は、これが取るべきアプローチとなります。
Applicationオブジェクト
NPSAppManagedは、アプリケーションを開始と終了し、作成したさまざまなフォームの表示を管理するためのフレームワークを提供します。これにより、再帰深度問題が発生することはありません。
特別な理由がない限り、NPSAppManagedはほぼ間違いなくアプリケーションを管理するための最良の方法です。
NPSAppクラスとは異なり、独自のメインループを作成する必要はありません。NPSAppManagedは、アプリケーションの各フォームの表示を管理します。 Formオブジェクトを設定し、NPSAppManagedインスタンスの run()メソッドを呼び出すだけです。
NPSAppManagedにフォームを管理させる
FormオブジェクトをNPSAppManagedインスタンスに登録するには、次の3つの方法があります。
NPSAppManaged.addForm(id, FormClass, ...)
addForm()メソッドは新しいフォームが作成し、NPSAppManagedインスタンスに登録します。 Formオブジェクトにweakref.proxyを返します。 idは、フォームを一意に識別する文字列です。 FormClassは、作成するフォームのクラスである必要があります。 追加の引数はすべて、フォームのコンストラクターに渡されます。 フォームへの個別の参照を他の場所に保存しない場合は、addForm()メソッド使用するようにしてください。
NPSAppManaged.addFormClass(id, FormClass, ...)
addFormClass()メソッドは、インスタンスではなくFormClassで与えたフォームのクラスを登録します。 idは、フォームを一意に識別する文字列です。編集するたびに新しいインスタンスが作成されます。 追加の引数は、フォームが作成されるたびにフォームのコンストラクターに渡されます。
NPSAppManaged.registerForm(id, form)
registerForm()メソッドは、addForm()メソッドとは対照的に、NPSAppManaged内にweakref.proxyのみを格納します。
idは、フォームを一意に識別する文字列です。formはFormオブジェクトを与えます。
NPSAppManagedインスタンスに登録されているすべてのフォームは、self.parentAppとして制御アプリケーションにアクセスできます。
何らかの理由でフォームを削除する必要がある場合は、removeForm()メソッドを使用できます。
NPSApplicationManagedアプリケーションの実行
NPSApplicationManagedアプリケーションを実行するためには、run()メソッドを呼び出します。
run()メソッド
NPSAppManagedアプリケーションのメインループを開始します。 run()メソッドは、IDとして"MAIN"が与えられているデフォルトのフォームをアクティブにします。
NPSAppManaged.STARTING_FORM
何らかの理由でデフォルトフォームの名前を変更する必要がある場合は、NPSAppManaged.STARTING_FORMプロパティ を変更します。
アプリケーションが実行されると、次のメソッドがユーザーに表示されるフォームを制御します。
NPSAppManaged.setNextForm(formid)
現在のフォームが終了したときに表示されるフォームを設定します。
NPSAppManaged.setNextFormPrevious()
現在のフォームが履歴内の前のフォームに終了したときに表示されるフォームを設定します
NPSAppManaged.switchForm(formid)
現在のフォームの終了ロジックをバイパスして、名前付きフォームにすぐに切り替えます。
NPSAppManaged.switchFormPrevious()
すぐに履歴内の前のフォームに切り替えます。
NPSAppManagedの詳細説明
すべてのフォームの準備ができてNPSAppManagedインスタンスに登録されたら、run()メソッドを呼び出す必要があります。
run()メソッドは、IDに"MAIN"が与えられているデフォルトのフォームをアクティブにします。このデフォルトのIDは、NPSAppManagedクラスのインスタンス変数 STARTING_FORM をセットすることで変更できます。
その後、次に表示されるフォームは、インスタンス変数NEXT_ACTIVE_FORMで指定されたフォームになります。フォーム編集ループが終了するたびに、ここで指定されたフォームがアクティブになります。 NEXT_ACTIVE_FORMがNoneの場合、メインループは終了します。 NEXT_ACTIVE_FORMは、NPSAppManagedクラスのsetNextForm(formid)メソッドを呼び出して設定する必要があります。
フォームがNEXT_ACTIVE_FORMを制御するために使用する必要がある3つのメカニズムがあります。
特別なメソッド activate() を持たないNPSAppManagedに登録されたすべてのフォームにafterEditing()メソッドがあれば、呼び出されます。 NEXT_ACTIVE_FORMを決定するロジックはここにあります。 NEXT_ACTIVE_FORMは、アプリケーションのsetNextForm(formid)メソッドを呼び出して設定する必要があります。ユーザーが[OK]または[CANCEL]ボタンを選択することを期待している場合、これは画面を切り替えるための推奨される方法です。
アプリケーションメソッドswitchForm(formid)を使用すると、アプリケーションは現在のフォームの編集をすぐに停止し、指定されたフォームに切り替えます。フォームのタイプによっては、フォームに関連付けられているロジックもバイパスされる場合があります。
NPSAppManagedに登録されたフォームには、通常の edit()メソッドの代わりにNPSAppManagedが呼び出す activate()メソッドを指定できます。これには、追加のロジックを含めることができます。これは推奨される方法ではありませんが、柔軟性を高めることができます。この場合、明示的に呼び出さない限り、通常の edit()メソッドは呼び出されないことに注意してください。たとえば、activate()メソッドは次のようになります。
code: python
def activate(self):
self.edit()
self.parentApp.setNextForm(None)
これにより、フォームが完了した後にメインループが終了します。
NPSAppManagedが提供する追加サービス
次のメソッドは、NPSAppManagedをサブクラス化することで簡単にオーバーライドできます。
デフォルトでは、何もしません。
NPSAppManaged.onInMainLoop()
アプリケーションの実行中に各画面間で呼び出されます。最初の画面の前には呼び出されません。
NPSAppManaged.onStart()
このメソッドをオーバーライドして、初期化を実行します。必要に応じて、ここでアプリケーションのフォームを設定できます。
NPSAppManaged.onCleanExit()
このメソッドをオーバーライドして、アプリケーションがエラーなしで終了しているときにクリーンアップを実行します。
NPSAppManaged.keypress_timeout_default
これが設定されている場合、keypress_timeoutがこれに設定された新しいフォームが作成されます。ただし、フォームが属するアプリケーションを知っている場合、つまり、作成時にparentApp=が渡されている必要があります。 NPSAppManagedを使用している場合、これは自動的に行われます。
NPSAppManaged.while_waiting()
アプリケーションには、while_waiting()メソッドを含めることもできます。これは自由に定義およびオーバーライドでき、アプリケーションがユーザー入力を待機している間に呼び出されます(フォームのwhile_waiting()メソッドを参照)。
NPSAppManaged._internal_while_waiting()
このメソッドは、npyscreenによる内部使用のためのものです。
NPSAppManaged.switchForm(formid)
現在のフォームの編集をすぐに停止し、指定されたフォームに切り替えます。
NPSAppManaged.switchFormPrevious()
すぐに履歴内の前のフォームに切り替えます。
NPSAppManaged.resetHistory()
これまでに表示したフォームの履歴をクリアします。
NPSAppManaged.getHistory()
これまでに表示したフォームの履歴のリストを取得する
NPSAppManagedクラスで管理されるフォームのメソッドとアトリビュート
NPSAppManagedによって呼び出されるフォームには、メソッドを指定できます
Form.beforeEditing()
フォームの編集ループが呼び出される前に呼び出されます
Form.afterEditing()
フォームが終了したときに呼び出されます
Form.activate()
このメソッドの存在は、既存の.beforeEditing.editおよびafterEditingメソッドを完全にオーバーライドします。
その他のアプリケーションクラス
NPSAppクラス
NPSAppクラスを使用するには、それをサブクラス化し、独自の main()メソッド定義を提供します。 アプリケーションを実行する準備ができたら、run()を呼び出し、メインループが実行されます。
NPSAppクラスは、npyscreen の内部で使用する抽象クラスとして使用しています。NPSAppクラスは最大限の柔軟性を提供しますが、他のほとんどすべての点でNPSAppManagedより劣っています。
新しいプロジェクトには使用しないようにしてください。
Formオブジェクト
Formオブジェクトは、ウィジェットを含む画面領域です。フォームは、ユーザーが編集しているウィジェットを制御し、ポップアップメニューや特定のキーを押したときに発生するアクションなどの追加機能を提供する場合があります。
フォームの作成
Formクラスには次のクラス変数があります。
code: python
DEFAULT_LINES = 0
DEFAULT_COLUMNS = 0
SHOW_ATX = 0
SHOW_ATY = 0
デフォルト値では、画面全体に表示され、左上隅に表示されるフォームが作成されます。フォームのサイズの制御の詳細については、コンストラクターに渡される引数を参照してください。
Formオブジェクトの作成は、次のように引数をコンストラクタに渡します。
code: python
Form(name=None, lines=0, columns=0, minimum_lines=24, minimum_columns=80)
name=
フォームに名前を付けます。一部のウィジェットについては、これによりタイトルが表示されます。
lines=0、columns=0minimum_lines=24、minimum_columns=80
フォームのサイズは、絶対サイズ(lines=およびcolumns=)または最小サイズ(minimum_lines=およびminimum_columns=)のいずれかで調整できます。デフォルトの最小値(24x80)は、端末の標準サイズを提供します。フォームがそのサイズに収まるように計画している場合、フォームをスクロールしなくても、ほとんどすべてのシステムで表示できるはずです。必要に応じて、一方の方向に絶対サイズを使用し、もう一方の方向に最小サイズを使用できることに注意してください。
Formクラスのコンストラクターはメソッド create()を呼び出します。このメソッドは、フォームにウィジェットを追加するためにオーバーライドする必要があります。
ウィジェットをフォームに配置
フォームにウィジェットを追加するには、次の方法を使用します。
Form.add(WidgetClass, ...)
WidgetClass引数にはウィジェットクラスを与えます、追加の引数はすべてウィジェット自体のコンストラクターに渡されます。ウィジェットクラスのインスタンスオブジェクトが返されます。
ウィジェットの位置とサイズは、ウィジェットのコンストラクターによって制御されます。ただし、Formクラスが提供するヒントがあります。ウィジェットの位置をオーバーライドしない場合、ウィジェットはフォームのnextrelxおよびnextrelyアトリビュートに従って配置されます。 nextrelyアトリビュートは、ウィジェットが配置されるたびに自動的に増加します。
次のように、自分で増やすこともできます。
self.nextrely + = 1
これにより、前のウィジェットと次に配置されたウィジェットの間に隙間ができます。
Form.nextrely
次に作成されるウィジェットを作成する y 位置。
Formクラスでは、各ウィジェットがフォームに追加されるときに、これを以前に作成されたウィジェットの下の行に設定します。
nextrelx
次に作成されるウィジェットを作成する x 位置。
その他の標準フォーム機能
Form.create()
このメソッドは、Formクラスのコンストラクターによって呼び出されます。デフォルトでは何もしません。サブクラスでオーバーライドするためにありますが、フォームにすべてのウィジェットを設定するのに最適な場所です。このオーバーライドしたcreate()メソッドで、 self.add()メソッド呼び出してウィジェットを追加してゆきます。
Form.while_editing()
このメソッドは、ユーザーがウィジェット間を移動するときに呼び出されます。サブクラスでオーバーライドして、あるウィジェットを別のウィジェットの値に基づいて変更するなどの操作を行うことを目的としています。
Form.adjust_widgets()
このメソッドの使用には十分注意してください。フォームの編集中にキーを押すたびに呼び出され、それ以上頻繁に呼び出されない可能性があるという保証はありません。デフォルトでは何も実行せず、オーバーライドすることを目的としています。頻繁に呼び出されるため、ここでのオーバーヘッドはアプリケーション全体の速度を低下させる可能性があります。
たとえば、端末ソフトウェアなどの環境によっては、フォーム全体の再描画は遅い操作となることがあり、そうした場合では、再描画には非常に慎重になります。再描画が必要かどうかをテストするコードを入力し、画面全体を再描画するのではなく、本当に変更する必要があるウィジェットのみを再描画するようにしてください。
フォームのparentAppにadjust_widgets()というメソッドもある場合、これも呼び出されます。
Form.while_waiting()
ユーザーがキーを押すのを待っている間にアクションを実行したい場合は、while_waiting()メソッドを定義できます。また、ミリ秒単位の値であるkeypress_timeoutアトリビュートを設定する必要があります。入力を待つときはいつでも、keypress_timeoutで指定された時間が経過すると、while_waiting()メソッドが呼び出されます。 npyscreenは、while_waiting()メソッドを正確に一定の間隔で呼び出することはしません。実際、ユーザーがキーを押し続けると、まったく呼び出されない可能性があることに注意してください。
フォームのparentAppにwhile_waiting()というメソッドがある場合、これも呼び出されます。
keypress_timeoutの値が10であるときは、ユーザーが他のアクションを実行しないと仮定すると、while_waiting()メソッドがほぼ毎秒呼び出されることを意味します。
Form.keypress_timeout
上記のwhile_waiting()メソッドを参照してください。
Form.set_value(value)
Formクラスの valueアトリビュートに、引数で与えられた値を格納してから、それを持つすべてのウィジェットのwhenParentChangeValue()メソッドを呼び出します。
フォームの表示と編集
Form.display()
フォーム上のすべてのウィジェットとフォーム自体を再描画します。
Form.DISPLAY()
フォームを再描画しますが、表示がリセットされていることを確認してください。これは遅い操作であり、可能であれば呼び出すことは避けてください。外部プロセスによって端末が中断された場合は、これを使用する必要がある場合があります。
Form.edit()
ユーザーが各ウィジェットの値をインタラクティブに編集できるようにします。 NPSAppManagedクラスを正しく使用している場合は、このメソッドを呼び出す必要はありません。可能であればこのメソッドを呼び出さないようにする必要がありますが、NPSAppManagedクラスを使用しない単純なアプリケーションを作成する場合はこのメソッドを使用する必要があります。このメソッドを直接呼び出すことは、GUIアプリケーションでモーダルダイアログボックスを作成することに似ています。可能な限り、このメソッドを内部API呼び出しと見なしてください。
フォームが終了するとき
フォームは、いくつかの理由で編集モードを終了する場合があります。 NPSAppManagedアプリケーションでは、制御アプリケーションによってフォームが終了する場合があります。
ただし、editingアトリビュートをFalseに設定すると、フォームが終了します。
標準フォームクラス
Formクラス
基本的なFormクラス。フォームを編集するとき、ユーザーは右下隅にある[OK]ボタンを選択して終了できます。
デフォルトでは、フォームが端末全体に出力されます。
Popupクラス
ポップアップは、デフォルトサイズが小さいフォームです。
ActionForm
ActionFormクラスは、[OK]ボタンと[CANCEL]ボタンを作成します。どちらかを選択すると、フォームが終了します。on_ok()メソッドやon_cancel()メソッドは、フォームが終了するときに呼び出されます(ユーザーがこれらのボタンの1つを選択したと仮定します)。したがって、サブクラスは、これらのメソッドの一方または両方を便利にオーバーライドできますが、デフォルトでは何も実行されません。
on_ok()
[OK]ボタンが押されたときに呼び出されます。このメソッドでeditingアトリビュートをTrueに設定すると、フォームの編集が中止されます。
on_cancel()
[CANCEL]ボタンが押されたときに呼び出されます。このメソッドで editingアトリビュートをTrue に設定すると、フォームの編集が中止されます。
ActionFormV2クラス
バージョン4.3.0の新機能。
このActionFormV2クラスは、上記の ActionForm と同様に動作しますが、コードははるかに簡潔です。サブクラス化する方がはるかに簡単なはずです。
最終的に、ActionFormV2クラス ActionFormクラスを完全に置き換えられる可能性があります。
ActionFormMinimal
バージョン4.4.0の新機能。
ActionFormMinimalには、[OK]ボタンのみがあります。特別な状況で使用するためにユーザーの要求に応じて追加されます。
ActionPopup
ActionFormの小さいバージョン。
SplitForm
SplitFormクラスには、中央を横切る水平線があります。 get_half_way() メソッドは、描画された場所を示します。
draw_line_at
このアトリビュートは、画面上で線を引く位置を定義します。 draw_line_at= をコンストラクターに渡すことで設定できます。または、get_half_way()メソッドによって返される値に自動的に設定されます。
get_half_way()
フォームの中央にあるバーの y 座標を返します。実際、この形式のサブクラスでは、y 座標が実際に形式の途中にある必要がある特別な理由はなく、サブクラスは都合のよい値を返す場合があります。
MOVE_LINE_ON_RESIZE
このアトリビュートは、フォームのサイズが変更されたときにラインの位置を移動するかどうかを指定します。ラインより下のウィジェットも移動する必要があるためです。おそらく、このフォームのサブクラスのオーバーライドされたサイズ変更メソッドでは、この値はデフォルトでFalseに設定されています。
FormWithMenus
Formクラスに似ていますが、ポップアップメニューの追加機能を提供します。
フォームに新しいメニューを追加するには、new_menu()メソッドを使用します。これにより、メニューが作成され、プロキシが返されます。
詳細については、以下のメニューのセクションを参照してください。
ActionFormWithMenus
ActionFormクラスに似ていますが、ポップアップメニューの追加機能を提供します。
フォームに新しいメニューを追加するには、new_menu() メソッドを使用します。これにより、メニューが作成され、プロキシが返されます。
詳細については、以下のメニューのセクションを参照してください。
ActionFormV2WithMenus
バージョン4.3.0の新機能。
ActionFormV2WithMenusは、上記の ActionForm と同様に動作しますが、コードははるかに簡潔です。サブクラス化する方がはるかに簡単なはずです。
最終的に、ActionFormV2WithMenusはActionFormWithMenusを完全に置き換えられる可能性があります。
FormBaseNew
このフォームには、デフォルトで[OK]または[CANCEL]ボタンがありません。追加されたのメソッドpre_edit_loop()およびpost_edit_loop()は、フォームが編集される前後に呼び出されます。デフォルトでは何もしません。このクラスは、より複雑なユーザーインターフェイスのベースとして意図されています。
pre_edit_loop()
フォームが編集される前に呼び出されます。
post_edit_loop()
編集ループが終了した後に呼び出されます。
FormBaseNewWithMenus
FormBaseNewのメニュー対応バージョン。
Muttのようなフォーム
FormMutt
TUIメールクライアント mutt やTUIチャットクライアント irssiなどのプログラムのユーザーインターフェイスに触発されたこのフォームは、4つのデフォルトウィジェットを定義します。 wStatus1
これは画面の上部にあります。 STATUS_WIDGET_CLASSアトリビュートを変更することで、使用するウィジェットのタイプを変更できます(これは両方のステータス行に使用されることに注意してください)。
wStatus2
これは、画面の最後から2番目の行を占めます。 STATUS_WIDGET_CLASSアトリビュートを変更することで、使用するウィジェットのタイプを変更できます(これは両方のステータス行に使用されることに注意してください)。
wMain
これは、wStatus1とwStatus2の間の領域を占め、MultiLineウィジェットです。 FormMuttをサブクラス化し、MAIN_WIDGET_CLASSクラス変数を変更することにより、ここに表示されるウィジェットのタイプを変更できます。
wCommand
このフィールドは、画面の最後の行を占めます。 COMMAND_WIDGET_CLASSクラス変数を変更することにより、使用するウィジェットのタイプを変更できます。
デフォルトでは、wStatus1とwStatus2は編集可能にFalseに設定されています。
FormMuttActive、FormMuttActiveWithMenus、FormMuttActiveTraditional、FormMuttActiveTraditionalWithMenus
これらのクラスは、より複雑なアプリケーションの作成を容易にすることを目的としています。各クラスは、追加のクラスNPSFilteredDataBase、ActionControllerSimple、TextCommandBox、TextCommandBoxTraditionalを使用します。
muttやirssiなど一般的なUnixスタイルのTUIアプリケーションには、リストやグリッドで配置された時刻を備えた中央の画面、下部にコマンドライン、およびいくつかのステータスラインがあります。
これらのクラスを使用すると、同様のフォームを簡単に設定できます。 FormMuttActiveクラスとFormMuttActiveTraditionalクラスの違いは、後者では、ユーザーが実際に編集するウィジェットは画面下部のコマンドラインだけであるということです。ただし、これらのウィジェットがコマンドラインを編集していない場合、キーの押下はディスプレイの中央にある複数行ウィジェットに渡され、ユーザーはスクロールして項目を選択できます。
画面に実際に表示されるものは、ActionControllerSimpleクラスによって制御されます。このクラスは、個々のウィジェットではなく、NPSFilteredDatabaseクラスによって格納されたデータをベースとして使用します。
詳細については、このドキュメントで後述するMuttのようなアプリケーションの作成に関するセクションを参照してください。
複数ページのフォーム
FormMultiPage
バージョン2.0pre63の新機能
この実験的なクラスは、複数ページのフォームのサポートを追加します。デフォルトでは、ページの最後のウィジェットから下にスクロールすると次のページに移動し、最初のウィジェットから上にスクロールするとページに戻ります。
display_pagesがTrueで、複数のページがある場合、デフォルトのクラスは現在のページを右下隅に表示します。 display_pages = Falseをコンストラクターに渡すこともできます。この表示に使用される色は、pages_label_colorアトリビュートに格納されています。デフォルトでは、これは"NORMAL"です。他の適切な値は、"STANDOUT"、"CONTROL"、または"LABEL"です。
繰り返しますが、これをコンストラクターに渡すことができます。
このクラスは実験的であることに注意してください。 APIはまだレビュー中であり、将来のリリースで変更される可能性があります。これは、フォームを動的に作成する必要があり、画面よりも大きい単一のフォームを作成する必要があるアプリケーションを対象としています。任意に大きなアイテムのリストを表示します。そのために、ウィジェットの複数行クラスははるかに効率的です。
このフォームに3つの新しいメソッドが追加されました。
FormMultiPage.add_page()
フォームの作成中に使用することを目的としています。これにより、新しいページが追加され、新しいウィジェットが追加される位置がリセットされます。追加されたページのインデックスが返されます。
FormMultiPage.switch_page()
このメソッドは、アクティブなページをインデックスで指定されたページに変更します。
FormMultiPage.add_widget_intelligent
このメソッドは、ウィジェットをフォームに追加します。現在のページに十分なスペースがない場合は、新しいページを作成してそこにウィジェットを追加しようとします。ユーザーが新しいページにもウィジェットが表示されないようにするオプションを指定した場合、このメソッドでも例外が発生する可能性があることに注意してください。
FormMultPageAction
バージョン2.0pre64の新機能
これはFormMultiPageクラスの実験的なバージョンであり、ActionFormクラスのon_ok()メソッドとon_cancel()メソッドを追加し、フォームの最後のページに[OK]ボタンと[CANCEL]ボタンを自動的に作成します。
FormMultiPageWithMenus
メニュー対応バージョンのMultiPage。
FormMultiPageActionWithMenus
MultiPageActionのメニュー対応バージョン。
Menu
一部のフォームクラスは、ポップアップメニューの使用をサポートしています。理論的には、メニューはそれ自体でウィジェットとして使用できます。ドロップダウンメニューの代わりにポップアップメニューが、キーボード環境に適切で、利用可能な画面スペースを有効に活用し、さまざまなサイズの端末に簡単に展開できるようになります。
デフォルトでは、サポートするフォームには、ユーザーがメニューシステムを利用できることを示すマークと、メニューのリストへのショートカットが表示されます。フォームに複数のメニューがある場合は、それらすべてを一覧表示するルートメニューが表示されます。
メニューは通常、フォームのnew_menu()メソッドを呼び出すことによって作成されます。フォームに表示されるメニューのリストに、このショートカットが表示されます。メニューが作成された後、そのオブジェクトに対する次のメソッドが役立ちます。
バージョン2.0pre82には、このメソッドに引数shortcut = None が追加されています。
NewMenu.addItem(text="", onSelect=function, shortcut=None ,arguments=None ,keywords=None)
テキストは、メニューに表示される文字列である必要があります。 onSelectは、そのアイテムがユーザーによって選択された場合に呼び出される関数である必要があります。これは、循環参照を作成するnpyscreenの数少ない簡単な機会の1つです。代わりに、関数にプロキシを渡すことをお勧めします。可能な限り循環参照から保護するように努めましたが、これは、アプリケーションの構造を推測できない場合の1つにすぎません。
バージョン2.0pre82には、ショートカットを追加する機能が追加されています。
バージョン3.6以降、メニュー項目は引数のリストやキーワードの辞書で指定できます。
NewMenu.addItemsFromList(item_list)
この関数の引数は、リストまたはタプルである必要があります。この各要素は、各アイテムの作成に使用される引数のタプルである必要があります。このメソッドは非推奨であり、将来のバージョンで削除または変更される可能性があります。
NewMenu.addNewSubmenu(name=None, shortcut=None ,preDisplayFunction=None, pdfuncArguments=None, dfuncKeywords=None)
新しいサブメニューを作成します(プロキシを返します)。これは、サブメニューを作成するための推奨される方法です。
バージョン2.0pre82には、キーボードショートカットを追加する機能が追加されています。
バージョン3.7以降では、このメニューが表示される前に呼び出される関数と引数を定義できます。これは、メニューが表示された時点でメニューの内容を調整できることを意味する場合があります。ユーザーの要求に応じて追加されました。
NewMenu.addSubmenu(submenu)
既存のメニューをサブメニューとしてメニューに追加します。通常、addNewSubmenu()メソッドの方が適しています。npyscreen では、このあたらしいシステムはニューメニューシステムと呼ばれます。これは、私がこれまであまり満足していなかったドロップダウンメニューシステムに代わるものです。
フォームのサイズ変更
バージョン2.0pre88の新機能。
フォームのサイズが変更されると、現在画面に表示されているフォームにシグナルが送信されます。フォームがこれを処理するかどうかは、3つのことによって決定されます。
モジュール変数 npyscreen.DISABLE_RESIZE_SYSTEM を True に設定すると、フォームのサイズはまったく変更されません。
NewMenu.ALLOW_RESIZE
クラス変数 ALLOW_RESIZE が False に設定されている場合、フォームはそれ自体のサイズを変更しません。デフォルトはTrue です。
クラス変数 FIX_MINIMUM_SIZE_WHEN_CREATED は、フォームを作成時のサイズよりも小さくできるかどうかを制御します。デフォルトでは、これはFalseに設定されています。これは、10年以上の間、npyscreenはフォームのサイズが変更されないことを想定しており、多くのプログラムがフォームのサイズが変更されないという事実に依存している可能性があるためです。新しいコードを最初から作成する場合は、結果をテストしてフォームのサイズを変更してもアプリケーションがクラッシュしないことを確認すれば、この値をTrueに設定できます。
フォームのサイズが変更されると、フォームの新しいサイズが修正された後にresize()メソッドが呼び出されます。フォームはこのメソッドをオーバーライドして、ウィジェットを新しい場所に移動したり、フォームのレイアウトに関するその他の変更を適切に行うことができます。
NPSAppManagedシステムを使用する場合、フォームは表示される前に自動的にサイズ変更されます。
ウィジェット:基本機能
ウィジェットの作成
ウィジェットは、フォームのadd()メソッドの最初の引数としてクラスを渡すことによって作成されます。残りの引数は、ウィジェット自体のコンストラクターに渡されます。これらは、size、position、name、初期値などを制御します。
コンストラクター引数
name=
おそらく、各ウィジェットに名前(文字列)を付ける必要があります。必要に応じて、ウィジェットのラベルとして使用されます。
relx=、rely=
フォーム上のウィジェットの位置は、relxおよびrely整数によって制御されます。それらを指定する必要はありません。その場合、フォームはウィジェットを配置する場所を決定するために最善を尽くします。そのように選択した場合は、どちらか一方のみを指定できます(たとえば、通常はrelxを指定する必要はありません)。
relyまたはrelxに負の値を指定すると、ウィジェットはフォームの下部または右側を基準にして配置されます。フォームのサイズが変更された場合、npyscreenはウィジェットを可能な限り所定の位置を維持しようとします。。
width=、height=、max_width=、max_height=
デフォルトでは、ウィジェットは、意味がない場合を除いて、使用可能なすべてのスペースを右下に埋めるように拡張されます。たとえば、1行のテキストは複数行を必要としないため、1より大きい値を与えないでください。したがって、ウィジェットのサイズを変更するには、別のmax_widthまたはmax_heightを指定します。 max_* を使用する方がおそらく良いでしょう。スペースが狭くして、ウィジェットを大きくしすぎてもエラーは発生しませんが、ウィジェットを残りのスペースに押しつぶそうとします。
value=
ウィジェットが受け取る値valueは、ユーザーが変更できるものです。文字列、日付、選択されたアイテム、ファイル名です。 これは、valueアトリビュートの初期設定です。
values=
ウィジェットがユーザーに候補のリストからの選択させる場合、選択候補ここで指定できます。これは、valuesアトリビュートの初期設定です。
editable=True
ユーザーがウィジェットを編集できるようにするかどうか。 editableアトリビュートの初期設定。
hidden=False
ウィジェットが表示されているかどうか。 hiddenアトリビュートの初期設定です。
color=’DEFAULT’、labelColor=’LABEL'
ウィジェットの表示方法に関するヒントをカラーマネジメントシステムに提供します。
詳細については、「色の設定」を参照してください。
scroll_exit=False、slow_scroll=False、exit_left、exit_right
これらは、ユーザーが複数行のウィジェットを操作する方法に影響します。 scroll_exitは、ユーザーが最初または最後のアイテムから、前または次のウィジェットに移動できるかどうかを決定します。 slow_scrollは、スクロールするウィジェットが、画面全体ではなく、一度に1行ずつスクロールすることを意味します。
フラグ引数exit_left 、 exit_rightは、ユーザーが左矢印キーと右矢印キーを使用してウィジェット選択を終了できるかどうかを指定します。
ウィジェットの使用と表示
すべてのウィジェットには次のメソッドがあります。
display()
ウィジェットを再描画し、cursesに画面を更新するように指示します。
update(clear=True)
ウィジェットを再描画しますが、cursesに画面を更新するように指示しません(すべてのウィジェットを更新してから、ウィジェットが置かれているフォームにcursesに画面を一度に再描画するように指示させる方が効率的です)。
ほとんどのウィジェットは、フラグ引数clearを受け入れます。これは、ウィジェットが再描画する前に、占有する領域を最初に空白にするかどうかに影響します。
when_parent_changes_value()
親フォームのset_value(value) メソッドが呼び出されるたびに呼び出されます。
when_value_edited()
ウィジェットの編集中にその値が変更されたときに呼び出されます。つまりキーを押した後。これを無効にするには、check_value_changeアトリビュートをFalseに設定します。
この関数は、自分で使用するためにオーバーライドできます。
when_cursor_moved()
ウィジェットの編集中にカーソルが移動したときに呼び出されます。check_cursor_moveアトリビュートをFalseに設定することにより、このチェックを無効にできます。
この関数は、自分で使用するためにオーバーライドできます。
edit()
ユーザーがウィジェットを操作できるようにします。ユーザーがウィジェットを離れると、メソッドが戻ります。ほとんどの場合、このメソッドを自分で呼び出す必要はありません。ほとんどの場合、これはnpyscreenの内部APIの一部と見なす必要があります。
set_relyx()
バージョン4.3.0の新機能
フォーム上のウィジェットの位置を設定します。 yまたはxが負の値の場合、npyscreenはフォームの下端または右端を基準にして配置しようとします。これは、フォームが定義した可能性のあるマージンを無視することに注意してください。
safe_to_exit()
バージョン4.10.0の新機能
このメソッドは、ユーザーがウィジェットを終了しようとする前に、デフォルトのハンドラーによって呼び出されます。これを許可する必要がある場合はTrueを返し、許可しない場合はFalseを返す必要があります。このメソッドをオーバーライドして、ユーザーが終了できるようにする前に、フィールドの内容の検証を実行できます。
タイトル付きウィジェット
多くのウィジェットは2つの形式で存在し、1つはラベル付き、もう1つはラベルなしです。たとえば、TextboxとTitleTextです。ウィジェット作成時にラベルが特に長い場合、ラベルを独自の行に配置することができます。
追加のコンストラクター引数には次のものがあります。
use_two_lines=
TrueまたはFalseの場合、ウィジェットが選択するものをオーバーライドします。
field_width=
(テキストフィールドの場合)-ウィジェットの入力部分の幅はどのくらいにする必要がありますか?
begin_entry_at=16
ウィジェットのエントリ部分が開始する列
内部的にタイトルが付けられたウィジェットは、実際には(ラベル用の)テキストボックスであり、他の種類のウィジェットが必要です。アトリビュート label_widgetとentry_widgetを使用して、個別のウィジェットにアクセスできます。ただし、結合されたウィジェットのvalueアトリビュートとvaluesアトリビュートは期待どおりに機能するはずなので、必要になることはありません。
独自のウィジェットを作成する
すべてのウィジェットは、Widgetクラスから継承する必要があります。
calculate_area_neeeded()
この関数は、ウィジェットに必要な行と列の数を尋ねるために呼び出されます(最小限の表示の場合)。正確に2つの数値を持つタプルを返す必要があります。いずれかの引数に0を返すと、ウィジェットが使用可能な場合は、ディスプレイ上の残りのスペースをすべて指定することになります。
画面にテキストを書き込む場合は、cursesを直接使用するのではなく、npyscreenの関数を使用する必要があります。(要確認)
add_line(realy ,realx, unicode_string, attributes_list, max_columns, force_ascii=False)
この関数は、ディスプレイに1行のテキストを追加します。 realyとrealxは、フォーム上の絶対位置です。 attributes_listは、各文字に適用する必要があるアトリビュートのリストです。それらすべてに同じアトリビュートが必要な場合は、make_attributes_list()メソッドを使用して、適切な長さのリストを作成します。
make_attributes_list(unicode_string, attribute)
便利な機能。指定されたunicode_stringの長さのリストを再調整し、リストの各エントリにアトリビュートのコピーが含まれます。
resize()
バージョン4.3.0の新機能
このメソッドをオーバーライドして、ウィジェットのサイズが変更されたときに必要なアクションを実行できます。
ウィジェット:テキストの表示
Textfild、TitleText
任意の長さですが、1行のテキスト-基本的なエントリウィジェット。
FixedText、TitleFixedText
1行のテキストですが、Textboxの編集機能は削除されています。
PasswordEntry、TitlePassword
テキストボックスですが、valueアトリビュートの文字が表示されないように変更されています。
Autocomplete
これはテキストボックスですが、追加機能があります。ユーザーがTabキーを押すと、ウィジェットはユーザーが入力した内容を完了しようとし、必要に応じてオプションを選択できるようにします。呼び出されるメソッドは auto_complete() です。
もちろん、ここではコンテキストがすべてです。したがって、オートコンプリートは役に立ちませんしかし、サブクラス化されるものとして意図されています。
例については、FilenameクラスとTitleFilenameクラスを参照してください。
Filename、TitleFilename
ユーザーが入力したファイル名またはパスを完成させようとするテキストボックス。
これはオートコンプリートウィジェットの例です。
FilenameCombo、TitleFilenameCombo
バージョン2.0pre82の新機能。
これは、ファイルを選択するためのより高度な方法です。
MultiLineEdit
このウィジェットを使用すると、ユーザーは数行のテキストを編集できます。
Pager、TitlePager
このウィジェットはテキスト行を表示し、ユーザーがそれらをスクロールできるようにしますが、編集することはできません。表示するテキストはvaluesアトリビュートに保持されます。
Textbox の補足説明
display_value(val)
valueアトリビュートの値の表示方法を制御します。テキストウィジェットは他の複合ウィジェット(ほとんどの複数行クラスなど)で使用されるため、このメソッドはしばしばオーバーライドされます。
show_brief_message()
ビープ音を鳴らして、ユーザーに簡単なメッセージを表示します。一般に、これを行うためのより良い方法がありますが、これは、たとえばオートコンプリートクラスでエラーを表示する場合などに役立つ場合があります。
ウィジェット:オプションの選択
MultiLine
ユーザーにオプションのリストを提供します。
オプションは、リストとしてvaluesアトリビュートに納する必要があります。valueアトリビュートは、ユーザーの選択のインデックスを格納します。インデックスではなく実際に選択された値を返したい場合は、get_selected_objects() メソッドを使用してください。
MultiLineとそれから派生したウィジェットの最も重要な機能の1つは、ユーザーがさまざまなタイプのオブジェクトを選択できるように簡単に適応できることです。
これには、display_value(self, val)をオーバーライドします。引数val は表示されるオブジェクトになり、この関数は画面に表示できる文字列を返す必要があります。
つまり、任意のタイプのオブジェクトのリストを渡すことができます。デフォルトでは、str() を使用して表示されますが、display_value()メソッドをオーバーライドすることで、適切と思われる方法で表示できます。
MultiLineを使用すると、ユーザーはエントリをフィルタリングすることもできます。 デフォルトでは、フィルター(filter)、クリアフィルター(clear filter)、次(next)および前(previous)は、キーl、L、n、p にバインドされます。現在の実装では、画面上で一致する行が強調表示されます。将来の実装では、他の行を非表示にするか、選択肢を提供する可能性があります。 filter_value()メソッドをオーバーライドすることで、フィルターの動作を制御できます。これは、引数としてインデックスを受け入れ(リストvaluesを検索します)、一致した場合はTrueを返し、それ以外の場合はFalseを返す必要があります。
バージョン2.0pre74から、そのallow_filteringアトリビュートをFalseに設定することにより、フィルタリングシステム全体を無効にすることができます。
これは、コンストラクターへの引数として渡すこともできます。
MultiLineウィジェットは、ディスプレイのさまざまな部分を処理する一連の他のウィジェットを保持するコンテナウィジェットです。すべてのMultiLineクラスには、_contained_widgetクラス変数があります。これは、ウィジェットの構築方法を制御します。クラス変数_contained_widget_heightは、各ウィジェットに指定する画面の行数を指定します。
TitleMultiLine
MultiLineウィジェットのタイトル付きバージョン。
MultiLineの独自のサブクラスを作成する場合は、このオブジェクトをサブクラス化し、_entry_typeクラス変数を変更することでTitleバージョンを作成できます。
MultiSelect、TitleMultiSelect
ユーザーにオプションのリストを提供し、ユーザーがそれらを複数選択できるようにします。
valueアトリビュートは、ユーザーが選択したインデックスのリストです。 MultiLineウィジェットと同様に、選択肢のリストはvalueアトリビュートに保存されます。
SelectOne、TitleSelectOne
機能的には、これらはMultilineクラスに似ていますが、MultiSelectウィジェットに似た表示がされます。
MultiSelectFixed、TitleMultiSelectFixed
MultiSelectのこれらの特別なバージョンはデータを表示することを目的としていますが、Textfixedのように、ユーザーが実際にデータを編集することはできません。
MultiLineAction
この種のウィジェットの一般的な使用例は、ユーザーがリターンキー、スペースキーなどを押したときに、現在強調表示されているアイテムに対してアクションを実行することです。このクラスのメソッドactionHighlighted(self, act_on_this, key_press) をオーバーライドして、この種のウィジェットを提供します。このメソッドは、ユーザーがアイテムを選択したときに呼び出され(この場合、valueアトリビュートは実際には設定されません)、強調表示されたアイテムとユーザーが実際に押したキーが渡されます。
MultiSelectAction
これは、上記のMultiLineActionウィジェットに似ていますが、
actionSelected(self、act_on_these、keypress) メソッドも提供する点が異なります。
このメソッドはオーバーライドでき、ユーザーがセミコロン(;)を押すと呼び出されます。メソッドには、選択されたオブジェクトのリストとキー押下が渡されます。このウィジェットを便利にするために、デフォルトのキーバインドを調整することをお勧めします。
BufferPager、TitleBufferPager
バージョン2.0pre90の新機能
BufferPagerクラスは、Pagerクラスのサブクラスです。これは、Unix系プラットフォームで、
tail -fとほぼ同じ方法でユーザーにテキストを表示するように設計されています。
デフォルトでは、valuesアトリビュートはcollections.dequeクラスのインスタンスに設定されています。 maxlen=valueをコンストラクターに渡すことができます。そうでない場合、dequeオブジェクトのmaxlenは、クラス変数DEFAULT_MAXLENから取得されます。
これはデフォルトではNoneです。
補足説明: tail コマンド
tail コマンドは指定したファイルの最終行から数行を表示するコマンドです。
-fオプションを使用すると、ファイルの終わりに達したときにtail が停止するのではなく、
ファイルにデータが追加されるのを待ちます。
標準入力がパイプの場合、-fオプションは無視されますが、FIFOの場合は無視されません。
BufferPager.clearBuffer()
バッファをクリアします。
BufferPager.buffer(lines, scroll_end=True, scroll_if_editing=False)
含まれているdequeオブジェクトに行を追加します。 scroll_endがTrueの場合、バッファーの最後までスクロールします。 scroll_if_editingがTrueの場合、ユーザーが現在ページャーを編集している場合でも、最後までスクロールします。含まれているdequeオブジェクトが最大長で作成された場合、新しいデータによって古いデータが忘れられる可能性があります。
MultiLineEditable
バージョン3.9の新機能
MultiLineクラスに基づいて、ユーザーが編集できるアイテムのリスト。
get_new_value()
このメソッドは、リストの新しいアイテムを初期化するために使用できる空のオブジェクトを返す必要があります。デフォルトでは、空の文字列を返します。
MultiLineEditableTitle
MultiLineEditableのタイトル付きバージョン。クラス変数_entry_typeは、含まれているウィジェットのタイプを制御します。
MultiLineEditableBoxed
MultiLineEditableのボックス版。クラス変数 _entry_type は、含まれているウィジェットのタイプを制御します。
カスタムMultiselectウィジェット
MultiLineウィジェットは、ディスプレイのさまざまな部分を処理する一連の他のウィジェットを保持するコンテナウィジェットです。すべてのMultiLineクラスには、_contained_widgetクラス変数があります。これは、ウィジェットの構築方法を制御します。
クラス変数_contained_widget_heightは、各ウィジェットに指定する画面の行数を指定します。
バージョン3.4以降、selectedアトリビュートを持つ含まれているウィジェットの処理は異なります。行が選択されているウィジェットでは selectedアトリビュートがTrueに設定され、それ以外の場合はFalseに設定されます。ウィジェットの.importantアトリビュートは、現在のフィルターに含まれているかどうかに応じて、TrueまたはFalseに設定される場合もあります(上記を参照)。
選択されたアトリビュートを持たないウィジェットでは、各行の値がnameアトリビュートに入力され、行が選択されているかどうかがvalueアトリビュートに入力されます。これは、標準の複数選択ウィジェットがチェックボックスを使用して各行を表示するという事実の遺産です。
バージョン4.8.7以降、複数行ウィジェットは、メソッドset_is_line_important()、set_is_line_bold()、およびset_is_line_cursor()を使用して、各行の表示を制御します。これらのメソッドには、問題のウィジェットオブジェクトとブール値が渡されます。
これらはオーバーライドされることを目的としています。
ウィジェット:ツリー表示
ツリーデータの表現
TreeData
TreeDataクラスは、ツリーオブジェクトを表すために使用されます。ルートノードを含むツリーの各ノードは、NPSTreeDataインスタンスです。各ノードには、親または子という独自のコンテンツがあります。
各ノードのコンテンツは、作成時に設定されるか、set_content()メソッドを使用して設定されます。
get_content(): このメソッドはコンテンツを返します。
get_content_for_display():このメソッドは、ツリーを表示するウィジェットによって使用されます。ウィジェットは、コンテンツを表すためにユーザーに表示できる文字列を返すことを期待しています。このメソッドをオーバーロードすることをお勧めします。
new_child(content):このメソッドは、新しい子ノードを作成します。
selectable:このアトリビュートが True の場合、ユーザーは値をselected(選択済み)としてマークできます。これはMLTreeMultiSelectウィジェットによって使用され、デフォルトではTrueです。
ignore_root:このアトリビュートは、ツリーのルートノードをユーザーに表示するかどうかを制御します。
Expanded:このアトリビュートは、ノードに子があると仮定して、ツリーのこのブランチを展開するかどうかを制御します
sort:このアトリビュートは、ツリーをソートするかどうかを制御します。
sort_function:このアトリビュートで指定された関数は、ツリーが表示されているときにツリーをソートするためのキーとして使用されます。
walk_tree(only_expanded=True, ignore_root=True, sort=None, sort_function=None):このメソッドはツリーを反復処理します。標準の並べ替えおよび並べ替え関数をオーバーライドして、展開済みとしてマークされているツリーのノードのみを反復処理するかどうかを決定できます。
Trees
MLTree、MLTreeAction
このクラスのvaluesアトリビュートには、NPSTreeインスタンスを格納する必要があります。ただし、必要に応じて、このクラスのconvertToTree()メソッドをオーバーライドできます。このメソッドは、NPSTreeインスタンスを返す必要があります。この関数は、値が割り当てられるたびに自動的に呼び出されます。
デフォルトでは、このクラスはTreeLineウィジェットを使用してツリーの各行を表示します。派生クラスの場合これは、クラス変数_contained_widgetsを変更することで変更できます。
クラス変数_contained_widget_heightは、各ウィジェットに指定する画面の行数を指定します。
MLTreeAnnotated、MLTreeAnnotatedAction
デフォルトでは、このクラスはTreeLineAnnotatedウィジェットを使用してツリーの各行を表示します。派生クラスの場合これは、クラス変数_contained_widgetsを変更することで変更できます。
MLTreeMultiSelect
このクラスを使用すると、ツリーの複数のアイテムを選択できます。そのノードで選択可能なアトリビュートを設定することにより、ユーザーが選択できるNPSTreeDataのノードを選択できます。
get_selected_objects(self, return_node=True)メソッドは、選択されたノードをリストするジェネレーターオブジェクトを返します。 return_nodeがTrueの場合、実際のノード自体が生成されます。それ以外の場合は、代わりにnode.getContent()の値が生成されます。
select_cascadesアトリビュートがTrueの場合(作成時に引数select_cascadesを渡すか、直後にアトリビュートを設定することで設定できます)、ノードを選択すると、選択したノードの下にある選択可能なノードが自動的に選択されます。これはデフォルトでTrueに設定されています。
選択されたノードのアトリビュートもTrueに設定されているため、必要に応じてツリーをウォークスルーしてノードを見つけることができます。
各行を表示するために使用されるウィジェットはTreeLineSelectableです。
MLTreeMultiSelectAnnotated
TreeLineSelectableAnnotatedを表示ウィジェットとして使用するMLTreeMultiSelectクラス
非推奨のツリークラス
NPSTreeData
TreeDataクラスを優先して非推奨。 NPSTreeDataクラスは、ツリーオブジェクトを表すために使用されます。ルートノードを含むツリーの各ノードは、NPSTreeDataインスタンスです。各ノードには、親または子という独自のコンテンツがあります。
setContent():各ノードのコンテンツは作成時に設定されるか、setContent()メソッドを使用して設定されます。
getContent():このメソッドはコンテンツを返します。
getContentForDisplay():このメソッドは、ツリーを表示するウィジェットによって使用されます。ツリーは、コンテンツを表すためにユーザーに表示できる文字列を返すことを期待しています。このメソッドをオーバーロードすることをお勧めします。
newChild(content):メソッドは、新しい子ノードを作成します。
selectable:このアトリビュートが True の場合、ユーザーは値をselected(選択済み)としてマークできます。これはMLTreeMultiSelectウィジェットによって使用され、デフォルトではTrueです。
MultiLineTree、SelectOneTree、MultiLineTree
これらのウィジェットはすべて、valuesアトリビュートにNPSTreeが含まれていることを想定していることを除いて、非ツリークラスと非常によく似た方法で機能します。他の大きな違いは、それらの value アトリビュートには、選択された値のインデックスは含まれていませんが、選択された値自体が含まれていることです。ただし、これらのクラスは、大幅に改善されたMLTreeクラスとMLTreeActionクラスを優先して非推奨になりました。
MultiLineTreeNew、MultiLineTreeNewAction
これらのクラスは、古いコードとの互換性のためにのみ提供されています。新しいクラスはMLTreeと関連クラスを使用する必要があります
このクラスのvaluesアトリビュートには、NPSTreeインスタンスを格納する必要があります。ただし、必要に応じて、このクラスのメソッドconvertToTreeをオーバーライドできます。このメソッドは、NPSTreeインスタンスを返す必要があります。この関数は、値が割り当てられるたびに自動的に呼び出されます。
デフォルトでは、このクラスはTreeLineAnnotatedウィジェットを使用してツリーの各行を表示します。派生クラスの場合これは、クラス変数_contained_widgetsを変更することで変更できます。
MutlilineTreeNewAnnotated、MultilineTreeNewAnnotatedAction
これらのクラスは、古いコードとの互換性のためにのみ提供されています。新しいクラスはMLTreeと関連クラスを使用する必要があります
デフォルトでは、このクラスはTreeLineAnnotatedウィジェットを使用してツリーの各行を表示します。派生クラスの場合これは、クラス変数_contained_widgetsを変更することで変更できます。
ウィジェット:Dates、Sliders、Combinationウィジェット
DateCombo、TitleDateCombo
これらのウィジェットを使用すると、ユーザーは日付を選択できます。日付の選択では、一時ウィンドウに表示されるMonthBoxクラスを使用して行われます。コンストラクターには、allowPastDate=False および allowTodaysDate=False の引数を渡すことができます。どちらも、ユーザーが選択できる内容に影響します。
コンストラクターは、引数 allowClear=True を受け入れることもできます。
ComboBox、TitleCombo
このボックスはTextboxのように見えますが、ユーザーは選択候補のリストからのみ選択できます。ユーザーが値を変更したい場合、一時ウィンドウに表示されます。 MultiLineウィジェットと同様に、value アトリビュートは選択候補のリストから選択したインデックスです。 ComboBoxウィジェットは、display_value(self, val)メソッドをオーバーロードすることによってカスタマイズすることもできます。
FilenameCombo、TitleFilenameCombo
これは、ファイル名を選択するためのコントロールを提供します。次の引数をコンストラクターに渡すことができます。
code: python
select_dir=False
must_exist=False
confirm_if_exists=False
sort_by_extension=True
これらは、いつでも設定できるウィジェットのアトリビュートにも対応しています。
Slider, TitleSlider
Sliderは水平スライダーを提供します。コンストラクターに対する次の追加の引数が役立ちます。
out_of=100:スライダーの最大値。
step=1:ユーザーが値を増減する増分。
lowest=-:ユーザーが選択できる最小値。lowest >= 0 負数は与えることができない 。
label = True:スライダーの横にテキストラベルを印刷するかどうか。その場合は、translate_value()メソッドを参照してください。
block_color=None:スライダーのレベルを示すブロックの色。デフォルトでは、スライダー自体の色と同じ値です。
これらのオプションはすべて、同じ名前のウィジェットのクラスのアトリビュートを設定することもできます。
ウィジェットの横に表示されるテキスト(label=Trueの場合)は、translate_value()メソッドによって生成されます。このメソッドは引数を受け取らず、文字列を返します。 Sliderオブジェクトをサブクラス化し、このメソッドをオーバーロードすることは理にかなっています。生成された文字列が固定長であることを確認することはおそらく理にかなっています。したがって、デフォルトのコードは次のようになります。
code: python
stri = "%s / %s" %(self.value, self.out_of)
l = (len(str(self.out_of)))*2+4
stri = stri.rjust(l)
return stri
SliderNoLabel, TitleSliderNoLabel
これらのクラスのスライダーにはラベルが表示されません。 label=Falseで通常のスライダーを使用するときと同じです。
SliderPercent、TitleSliderPercent
これらのスライダーは、ラベルにパーセンテージを表示します。小数点以下の桁数は、accuracyアトリビュート(精度)を設定するか、キーワードaccuracyをコンストラクターに渡すことで設定できます。デフォルトは2です。
ウィジェット:Grid
SimpleGrid
このウィジェットにより、スプレッドシートのような表示が提供されます。デフォルトでは、情報を(テキストフィールドのグリッドに)表示することのみを目的としています。ただし、さまざまな異なるデータを表示するように柔軟かつ簡単にカスタマイズできるように設計されています。将来のバージョンには、新しいタイプのグリッドが含まれる可能性があります。ウィジェットの作成時にcolumnsまたはcolumn_widthのいずれかを指定することで、グリッドの外観を制御できます。
将来的には、他の複数行クラスがこのクラスから派生する可能性があります。
edit_cell:カーソル位置を与えるアトリビュートです。
これは、行を指定してから列を指定するという(奇妙な)cursesのルールに従うことに注意してください。
values:2次元配列として指定する必要があります。
[* set_grid_values_from_flat_list(new_values, max_cols=None, reset_cursor=True):
このヘルパー関数はフラットリストを取得し、それをグリッドに表示します。
次の引数をコンストラクターに渡すことができます。
code: python
columns = None
column_width = None,
col_margin=1,
row_height = 1,
values = None
always_show_cursor = False
select_whole_line = False (new in version 4.2)
SimpleGridから派生したクラスは、次のクラス変数を変更したくなる場合があります。
code: python
_contained_widgets = textbox.Textfield
default_column_number = 4
additional_y_offset = 0 # グリッドの前にウィジェット内に残す追加のオフセット
additional_x_offset = 0 # グリッドの前にウィジェット内に残す追加のオフセット
select_whole_line # カーソルが置かれている行全体を強調表示します
GridColTitles
単純なグリッドと同様ですが、表示の最初の2行を使用して列のタイトルを表示します。これらは、構築時にcol_titles引数として、またはいつでもcol_titlesアトリビュートを設定することによって提供できます。いずれの場合も、文字列のリストを提供してください。
個々のグリッドセルの外観をカスタマイズする
一部のアプリケーションでは、コンテンツに応じて、含まれているグリッドウィジェットのアトリビュートをカスタマイズすることが望ましい場合があります。グリッドウィジェットは、セルの値を設定した後、セルのコンテンツが画面に描画される前に、custom_print_cell(actual_cell、display_value)というメソッドを呼び出します。パラメーターactual_cellは、表示に使用されている基になるウィジェットオブジェクトであり、display_valueは、セルのコンテンツ(display_valueメソッドの出力)として設定されているオブジェクトです。
次のコードは、この機能を使用してグリッドに表示されるテキストの色を調整する方法を示しています。この機能を提案してくれたJohanLundstr?mに感謝します。
code: python
class MyGrid(npyscreen.GridColTitles):
# セルの印刷方法を操作するには、custom_print_cellをオーバーライドする必要があります。
# この例では、セルの文字列値に応じてテキストの色を変更します。
def custom_print_cell(self, actual_cell, cell_display_value):
if cell_display_value =='FAIL':
actual_cell.color = 'DANGER'
elif cell_display_value == 'PASS':
actual_cell.color = 'GOOD'
else:
actual_cell.color = 'DEFAULT'
def myFunction(*args):
# Exampleのフォームを作成
F = npyscreen.Form(name='Example viewer')
myFW = F.add(npyscreen.TitleText)
gd = F.add(MyGrid)
# グリッドに values を追加すると、このコードは2x4グリッドを
# ランダムな PASS / FAIL 文字列でランダムに埋めます。
gd.values = []
for x in range(2):
row = []
for y in range(4):
if bool(random.getrandbits(1)):
row.append("PASS")
else:
row.append("FAIL")
gd.values.append(row)
F.edit()
if __name__ == '__main__':
npyscreen.wrapper_basic(myFunction)
ウィジェット:その他のコントロール
CheckBox、RoundCheckBox
これらは単一のオプションを提供します-ラベルは、タイトル付きウィジェットの場合と同様に、nameアトリビュートから生成されます。 value アトリビュートはTrueまたはfFlseのいずれかです。
ユーザーがチェックボックスの状態を切り替えると、whenToggled()メソッドが呼び出されます。
このメソッドはオーバーライドすることができます。
CheckboxBare
これにはラベルがなく、特別な状況でのみ役立ちます。 ユーザーの要望により追加されました。
CheckBoxMultiline、RoundCheckBoxMultiline
このウィジェットを使用すると、チェックボックスのラベルを複数行にすることができます。 ウィジェットの名前は、文字列のリストまたはタプルとして指定する必要があります。
これらのウィジェットを複数行ウィジェットの一部として使用するには、次のようにします。
code: python
class MultiSelectWidgetOfSomeKind(npyscreen.MultiSelect):
_contained_widgets = npyscreen.CheckBoxMultiline
_contained_widget_height = 2
def display_value(self, vl):
# this function should return a list of strings.
Button
機能的にはCheckBoxウィジェットに似ていますが、外観が異なります。Buttonは通常、フォームの[OK]ボタンと[CANCEL]ボタンなどに使用されますが、おそらくButtonPressタイプに置き換える必要があります。選択したときにボタンに表示される色は、ボタンの色の逆であるか、cursor_colorアトリビュートによって選択されます。この値は、コンストラクターに渡すこともできます。この値がNoneの場合、ボタンの色の逆が使用されます。
ButtonPress
トグルではなく、コントロールです。このウィジェットにはwhenPressed()メソッドがあり、独自のことを行うにはオーバーライドする必要があります。
コンストラクターは when_pressed_function引数を受け入れます。この引数に関数を指定すると、whenPressed()メソッドの代わりに呼び出されます。
注意。 when_pressed_functionは潜在的に危険です。ガベージコレクターが決して解放しない循環参照することができます。これがプログラムのリスクである場合は、このオブジェクトをサブクラス化し、代わりにwhen_pressed_function()メソッドをオーバーライドすることをお勧めします。
FormControlCheckbox
チェックボックスの一般的な使用法は、ユーザーに追加データを入力するオプションを提供することです。たとえば、「有効期限を入力してください」。
このような場合、フォームは追加のフィールドを表示する必要がある場合もありますが、そうでない場合もあります。 FormControlCheckboxはこれが簡単になります。
2つの方法が定義されています。
addVisibleWhenSelected(wg):
wgはウィジェットである必要があります。
このメソッドはウィジェットを作成しませんが、代わりに既存のウィジェットをFormControlCheckboxの制御下に置きます。 FormControlCheckboxを選択すると、ウィジェットが表示されます。
この方法で、必要な数のウィジェットを追加できます。
addInvisibleWhenSelected(wg)
この方法で登録されたウィジェットは、FormControlCheckboxが選択されていない場合にのみ表示されます。
AnnotateTextboxBase、TreeLineAnnotated、TreeLineSelectableAnnotated
AnnotateTextboxBaseクラスは、主に複数行のリストウィジェットで使用することを目的としており、表示される各アイテムにエントリ自体の左側に注釈を付ける必要がある場合に使用します。これらのクラスのAPIは、元々内部使用のみを目的としていたため、少し見苦しいものです。よりユーザーフレンドリーなバージョンは、今後のリリースで提供される可能性があります。 AnnotateTextboxBaseから派生したクラスは、以下を定義する必要があります。
ANNOTATE_WIDTH
このクラス変数は、テキスト入力ウィジェット自体の前に残すマージンを定義します。 TreeLineAnnotatedクラスでは、必要なマージンは動的に計算され、ANNOTATE_WIDTHは必要ありません。
getAnnotationAndColor
この関数は、注釈として表示する文字列と、表示時に使用する色の名前で構成されるタプルを返す必要があります。色は白黒ディスプレイでは無視されますが、すべての場合に提供する必要があり、デフォルトではクラスはこれをチェックしませんが、文字列はANNOTATE_WIDTHより長くすることはできません。
AnnotationColor()、AnnotationNoColor()
これらのメソッドは、画面に注釈を描画します。文字列のみを使用する場合、これらをオーバーライドする必要はありません。 npyscreenは、ディスプレイがカラー用に構成されている場合は1つを使用し、白黒用に構成されている場合はもう1つを使用するため、一方が変更された場合は、もう一方も変更する必要があります。
ウィジェット:Titledウィジェット
標準ウィジェットセットのほとんどの派生クラスには、ベースクラスと、ウィジェットの名前が付いたラベルも印刷する対応する派生クラスの2つの形式があります。
たとえば、TextfieldとTitleTextです。
タイトルクラスは、実際には、それ自体が固有なウィジェットではなく、ベースのウィジェットをラッパーしたウィジェットで、これは、動作を変更するときに混乱を引き起こす可能性があります。
一般に、これらの独自のウィジェットセットを作成するときは、最初にベースのウィジェットを作成してから、タイトル付きのバージョンを作成する必要があります。
code: python
class NewTextWidget(textbox.Textfield):
# このクラスのすべてのカスタムコードはここに配置する必要があります。
class TitleProductSearch(TitleText):
_entry_type = NewTextWidget
引数 begin_entry_at をコンストラクターに渡すことにより、子ウィジェットが画面上のどこに配置されるかを調整できます。デフォルトは16です。ウィジェットの作成時に引数 use_two_lines=True | False を渡すことで、ウィジェットがタイトルに別の行を使用するかどうかを指定することもできます。デフォルトの use_two_lines=None は、ラベルが長すぎない限り、タイトルと含まれているウィジェットを同じ行に配置します。
引数 labelColor=’LABEL’ を使用して、作成時にラベルの色を変更できます。使用しているテーマから任意の色名を指定できます。
作成後、TitleWidgetによって管理される2つのウィジェットには、オブジェクトのlabel_widgetアトリビュートとentry_widgetアトリビュートを介してアクセスできます。
Titled multilineウィジェット
複数行ウィジェットのTitledウィジェットで作成している場合は、代わりに、より多くの複数行機能をラップするTitleMultiLineクラスから継承する方がよいでしょう。
ウィジェット:Boxウィジェット
Boxウィジェットは、Titleウィジェットと同じように機能します。Boxウィジェットには、別のクラスのウィジェットが含まれています。
BoxBasic
BoxBasicは、オプションの名前とフッターが付いたボックスを画面に印刷します。これは、直接使用するためではなく、追加のウィジェットの基本クラスとして意図されています。
BoxTitle
BoxTitleは、TitleウィジェットとMultilineウィジェットのハイブリッドです。繰り返しになりますが、これは主に、より複雑なレイアウトの基本クラスとして意図されています。このクラスには、クラスの作成時にウィジェットをボックス内に配置する_contained_widgetアトリビュートがあります。 BoxTitleクラスでは、これは複数行ウィジェットです。ウィジェットのタイトルをコンストラクタに渡すことができます。パラメータには、name='...', footer=...のように与えます。この場合、ボックスのフッターのテキストを提供します。これらは、いつでも変更できるnameおよびfooterという名前のアトリビュートに対応しています。
entry_widgetアトリビュートは、ベースウィジェットへの直接アクセスする手段を提供します。
プロパティeditable、values、およびvalueを使用すると、entry_widgetアトリビュートに直接アクセスできます。
このウィジェットのコンストラクターには、引数contained_widget_argumentsを渡すことができます。これは、作成時にentry_widgetに渡される引数の辞書である必要があります。現時点では、この辞書の整合性のチェックは行われません。
これらのウィジェットの独自のウィジェットセットは、新しいタイトル付きウィジェットと同じ方法で作成できます。ベースウィジェットクラスを最初に作成してから、BoxBasicクラスを継承した派生クラスを作成します。
code: python
class NewMultiLineClass
# このクラスのすべてのカスタムコードはここに配置する必要があります。
class BoxTitle(BoxBasic):
_contained_widget = NewMultiLineClass
キーバインディングのすべて
動作概要
多くのオブジェクトは、ユーザーのキーの押下に基づいてアクションを実行できます。 このようなオブジェクトはすべて、内部クラスInputHandlerから継承します。 そのクラスは、handlersと呼ばれる辞書とcomplex_handlersと呼ばれるリストを定義します。 これらは両方とも、コンストラクターから呼び出されるset_up_handlers()メソッドによってセットアップされます。
ハンドラー
ハンドラーは次のようになります。
code: python
{curses.ascii.NL: self.h_exit_down,
curses.ascii.CR: self.h_exit_down,
curses.ascii.TAB: self.h_exit_down,
curses.KEY_DOWN: self.h_exit_down,
curses.KEY_UP: self.h_exit_up,
curses.KEY_LEFT: self.h_exit_left,
curses.KEY_RIGHT: self.h_exit_right,
"^P": self.h_exit_up,
"^N": self.h_exit_down,
curses.ascii.ESC: self.h_exit_escape,
curses.KEY_MOUSE: self.h_exit_mouse,
}
この辞書にキーとして定義されているキーが押されると(Control-Nは^N、Alt Nは!aなどの表記をサポートしています)、それに関連付けられた関数が呼び出されます。それ以上のアクションは実行されません。慣例により、ユーザー入力を処理する関数の接頭辞は h_ です。
complex_handlers
このリストには、(test_func, dispatch_func) のようなリストまたはタプルのペアが含まれている必要があります。
キーの辞書がhandlersの名前で指定されていない場合、各test_funcが実行されます。 Trueが返された場合、dispatch_funcが実行され、検索が停止します。
複雑なハンドラーは、たとえば、印刷可能な文字のみがテキストボックスに入力されるようにするために使用されます。それらは頻繁に実行されるため、できるだけ少なくし、できるだけ速く実行する必要があります。
ユーザーがウィジェットを編集していてキーが押されると、ハンドラー、次にcomplex_handlersを使用して、実行する関数を見つけようとします。ウィジェットで実行するアクションが定義されていない場合は、親フォームのハンドラーとcomplex_handlersがチェックされます。したがって、Enterキーのように、すでにバインドされているキーのハンドラーをオーバーライドする場合は、ウィジェットのハンドラーが優先されるため、ウィジェットが含まれているフォームではなく、ウィジェットでオーバーライドすることに注意してください。
独自のハンドラーを追加する
ユーザー入力を処理できるオブジェクトは、独自のキーバインディングの追加を支援するために次のメソッドを定義します。
add_handlers(new_handlers)
new_handlersは辞書である必要があります。
add_complex_handlers(new_handlers)
new_handlersはリストのリストである必要があります。
各サブリストは、(test_function, callback)のようなペアである必要があります
マウスサポートの強化
マウスイベントをより詳細に処理したいウィジェットは、
handle_mouse_event(self, mouse_event) メソッドをオーバーライドする必要があります。 mouse_eventはタプルであることに注意してください。
code: python
def handle_mouse_event(self, mouse_event):
mouse_id, x, y, z, bstate = mouse_event # 次の注意を参照してください
# ここに何かを記述します...
これは大部分は便利ですが、x と y は相対的な位置ではなく、絶対的な位置です。 そのため、提供されているヘルパー関数を使用して、これらの値をウィジェットを基準にした座標に変換する必要があります。 したがって、ほとんどのマウス処理関数は次のようになります。
code: python
def handle_mouse_event(self, mouse_event):
mouse_id, rel_x, rel_y, z, bstate = self.interpret_mouse_event(mouse_event)
# ここに何かを記述します...
マウスハンドラーは、ウィジェットがeditable(編集可能)である場合にのみ呼び出されます。 ごくまれに、編集不可能なウィジェットがマウスイベントに応答するようにしたい場合があります。 その場合、ウィジェットのself.interested_in_mouse_even_when_not_editableアトリビュートをTrueに設定できます。
マウスイベントの詳細については、Python 標準ライブラリ curses モジュールのドキュメントを参照してください。 色のサポート
色の設定
標準のウィジェットはすべて、モノクロ端末で完全に使用できます。 しかし、最近はカラフルな世界になっています。npyscreenを使用すると、curses が許す限りウィジェットを表示できます。
色はThemeManagerクラスによって処理されます。 通常、アプリケーションは1つのThemeManagerを使用することに固執する必要があります。これは、setTheme(ThemeManager)メソッドを使用して設定する必要があります。
code: python
pyscreen.setTheme(npyscreen.Themes.ColorfulTheme)
npyscreenで定義されているデフォルトのテーマには、npyscreen.Themes からアクセスできます。
基本的なテーマは次のようになります。
code: python
class DefaultTheme(npyscreen.ThemeManager):
default_colors = {
'DEFAULT' : 'WHITE_BLACK',
'FORMDEFAULT' : 'WHITE_BLACK',
'NO_EDIT' : 'BLUE_BLACK',
'STANDOUT' : 'CYAN_BLACK',
'CURSOR' : 'WHITE_BLACK',
'CURSOR_INVERSE': 'BLACK_WHITE',
'LABEL' : 'GREEN_BLACK',
'LABELBOLD' : 'WHITE_BLACK',
'CONTROL' : 'YELLOW_BLACK',
'IMPORTANT' : 'GREEN_BLACK',
'SAFE' : 'GREEN_BLACK',
'WARNING' : 'YELLOW_BLACK',
'DANGER' : 'RED_BLACK',
'CRITICAL' : 'BLACK_RED',
'GOOD' : 'GREEN_BLACK',
'GOODHL' : 'GREEN_BLACK',
'VERYGOOD' : 'BLACK_GREEN',
'CAUTION' : 'YELLOW_BLACK',
'CAUTIONHL' : 'BLACK_YELLOW',
}
WHITE_BLACK(white on black:背景色黒に前景色白)などの色は、ThemeManagerクラスのinitialize_pairs()メソッドで定義されます。 デフォルトでは、以下が定義されています。
code: python
('BLACK_WHITE', curses.COLOR_BLACK, curses.COLOR_WHITE),
('BLUE_BLACK', curses.COLOR_BLUE, curses.COLOR_BLACK),
('CYAN_BLACK', curses.COLOR_CYAN, curses.COLOR_BLACK),
('GREEN_BLACK', curses.COLOR_GREEN, curses.COLOR_BLACK),
('MAGENTA_BLACK', curses.COLOR_MAGENTA, curses.COLOR_BLACK),
('RED_BLACK', curses.COLOR_RED, curses.COLOR_BLACK),
('YELLOW_BLACK', curses.COLOR_YELLOW, curses.COLOR_BLACK),
)
WHITE_BLACK」`は常に定義されています。
さらに必要な場合は、ThemeManagerをサブクラス化し、クラス変数_colours_to_defineを変更します。標準のcurses以外の色を使用することはできますが、すべての端末がサポートしているわけではないため、npyscreenはデフォルトではサポートしていません。
アプリケーションのすべての色を無効にする場合、npyscreenは2つのヘルパー関数 disableColor()と enableColor() を定義します。
ウィジェットが色を使用する方法
ウィジェットが描画されると、アクティブなThemeManagerに適切な色を指示するように求められます。たとえば、LABEL は、ウィジェットのラベルに使用される色に付けられたラベルです。ThemeManagerは、default_colors辞書で関連する名前を検索し、適切な色のペアをcursesアトリビュートとして返します。このアトリビュートは、画面にウィジェットを描画するために使用されます。
多くの場合、個々のウィジェットには独自のcolorアトリビュートがあります(コンストラクターによって設定される場合があります)。これは通常 DEFAULT に設定されていますが、他の定義済みの名前に変更することもできます。このメカニズムでは、通常、個々のウィジェットでカラースキームの特定の部分のみを変更できます。
タイトルウィジェットは、ラベルの色の色を変更するために使用できるlabelColorアトリビュートも定義します。
カスタムカラーの定義(強くお勧めしません)
一部の端末では、カスタムカラー値を定義できます。 rxvt / urxvt はそのような端末の1つです。バージョン4.8.4以降では、これに対するサポートがテーママネージャークラスに組み込まれています。
クラス変数color_valuesは、カスタムカラー値を再定義するためにクラスが初期化されるときに使用されます。
code: python
_color_values = (
# 標準色の再定義
(curses.COLOR_GREEN, (150,250,100)),
# 別の色を定義する
(70, (150,250,100)),
)
注意:npyscreenは、アプリケーションの終了時にこれらの値をリセットすることはしません。
端末が実際にカスタムカラーをサポートしているかどうかを確実に判断することは不可能であるため、この機能の使用はお勧めしません。 この機能は、カスタムアプリケーションをサポートするために、ユーザーの要求に応じて追加されました。
簡単なメッセージと選択肢の表示
次の機能を使用すると、ユーザーに簡単なメッセージまたは選択肢を表示できます。
Notify()および関連するメソッドはnpyscreen / utilNotify.py に実装されています
このページの例は、この基本的なプログラムから作成されています。
code: python
import npyscreen
class NotifyBaseExample(npyscreen.Form):
def create(self):
key_of_choice = 'p'
what_to_display = 'Press {} for popup \n Press escape key to quit'.format(key_of_choice)
self.add(npyscreen.FixedText, value=what_to_display)
def exit_application(self):
self.parentApp.setNextForm(None)
self.editing = False
class MyApplication(npyscreen.NPSAppManaged):
def onStart(self):
self.addForm('MAIN', NotifyBaseExample, name='To be improved upon')
if __name__ == '__main__':
TestApp = MyApplication().run()
notify(message, title="Message", form_color='STANDOUT', wrap=True, wide=False)
この関数は、画面にメッセージを表示します。 ブロックされず、ユーザーは操作できません。
何か処理中の間、「お待ちください」などのメッセージを表示するために使用します。
../examples/notify/notify.pyスニペット
code: python
import npyscreen
import time
class NotifyExample(npyscreen.Form):
def create(self):
key_of_choice = 'p'
what_to_display = 'Press {} for popup \n Press escape key to quit'.format(key_of_choice)
self.add_handlers({key_of_choice: self.spawn_notify_popup})
self.add(npyscreen.FixedText, value=what_to_display)
def spawn_notify_popup(self, code_of_key_pressed):
message_to_display = 'I popped up \n passed: {}'.format(code_of_key_pressed)
npyscreen.notify(message_to_display, title='Popup Title')
time.sleep(1) # 例示のためのスリープ
notify_wait(message, title="Message", form_color='STANDOUT', wrap=True, wide=False)
この関数は、画面にメッセージを表示し、短時間ブロックします。 ユーザーはそれを操作できません。
../examples/notify/notify_wait.pyスニペット
code: python
class NotifyWaitExample(npyscreen.Form):
def create(self):
key_of_choice = 'p'
what_to_display = 'Press {} for popup \n Press escape key to quit'.format(key_of_choice)
self.add_handlers({key_of_choice: self.spawn_notify_popup})
self.add(npyscreen.FixedText, value=what_to_display)
def spawn_notify_popup(self, code_of_key_pressed):
message_to_display = 'I popped up \n passed: {}'.format(code_of_key_pressed)
npyscreen.notify_wait(message_to_display, title='Popup Title')
notify_confirm(message, title="Message", form_color='STANDOUT', wrap=True, wide=False, editw=0)
メッセージと[OK]ボタンを表示します。 ユーザーは必要に応じてメッセージをスクロールできます。 editwは、ダイアログが最初に表示されるときに選択されるウィジェットを制御します。 [OK]ボタンをすぐにアクティブにするには、1に設定します。
../examples/notify/notify_confirm.py スニペット
code: python
class NotifyConfirmExample(npyscreen.Form):
def create(self):
key_of_choice = 'p'
what_to_display = 'Press {} for popup \n Press escape key to quit'.format(key_of_choice)
self.add_handlers({key_of_choice: self.spawn_notify_popup})
self.add(npyscreen.FixedText, value=what_to_display)
def spawn_notify_popup(self, code_of_key_pressed):
message_to_display = 'You need to confirm me, so hit TAB, then ENTER'
npyscreen.notify_confirm(message_to_display, title= 'popup')
notify_ok_cancel(message, title="Message", form_color='STANDOUT', wrap=True, editw=0)
メッセージを表示し、ユーザーが[OK]ボタンを選択した場合はTrueを返し、ユーザーが [CANCEL]ボタンを選択した場合はFalseを返します。
../examples/notify/notify_ok_cancel.py スニペット
code: python
class NotifyOkCancelExample(npyscreen.Form):
def create(self):
key_of_choice = 'p'
what_to_display = 'Press {} for popup \n Press escape key to quit'.format(key_of_choice)
self.add_handlers({key_of_choice: self.spawn_notify_popup})
self.add(npyscreen.FixedText, value=what_to_display)
def spawn_notify_popup(self, code_of_key_pressed):
message_to_display = 'You have a choice, to Cancel and return false, or Ok and return true.'
notify_result = npyscreen.notify_ok_cancel(message_to_display, title= 'popup')
npyscreen.notify_wait('That returned: {}'.format(notify_result), title= 'results')
notify_yes_no(message, title="Message", form_color='STANDOUT', wrap=True, editw=0)
ボタンの名前が [Yes] と [No] であることを除いて、notify_ok_cancel に似ています。 TrueまたはFalseを返します。
selectFile(select_dir=False, must_exist=False, confirm_if_exists=True, sort_by_extension=True)
ユーザーがファイル名を選択するためのダイアログボックスを表示します。 呼び出されたディレクトリを初期フォルダとして使用します。 戻り値は、選択したファイルの名前です。
警告:このフォームは現在実験段階です。
../examples/notify/select_file.py スニペット
code: python
def create(self):
key_of_choice = 'p'
what_to_display = 'Press {} for popup \n Press escape key to quit'.format(key_of_choice)
self.add_handlers({key_of_choice: self.spawn_file_dialog})
self.add(npyscreen.FixedText, value=what_to_display)
def spawn_file_dialog(self, code_of_key_pressed):
the_selected_file = npyscreen.selectFile()
npyscreen.notify_wait('That returned: {}'.format(the_selected_file), title= 'results')
画面を空白にする
blank_terminal()
この関数は端末をブランクにします。 画面全体に表示されないフォームが表示されている場合は、必要になることがあります。
../examples/notify/blank_terminal.py スニペット
code: python
import npyscreen
import time
class BlankTerminalExample(npyscreen.Form):
def create(self):
key_of_choice = 'b'
what_to_display = 'Press {} to blank screen \n Press escape key to quit'.format(key_of_choice)
self.add_handlers({key_of_choice: self.initiate_blanking_sequence})
self.add(npyscreen.FixedText, value=what_to_display)
def initiate_blanking_sequence(self, code_of_key_pressed):
npyscreen.blank_terminal()
time.sleep(1.5)
npyscreen.notify('..and we\'re back', title='Phew')
time.sleep(0.75)
def exit_application(self):
self.parentApp.setNextForm(None)
self.editing = False
class MyApplication(npyscreen.NPSAppManaged):
def onStart(self):
self.addForm('MAIN', BlankTerminalExample, name='To show off blank_screen')
if __name__ == '__main__':
TestApp = MyApplication().run()
アプリケーションのサポート
オプションとオプションリスト
一般的な問題の1つは、オプションのリストをユーザーに表示することです。単純なアプリケーションでは、この目的のためにカスタム設計のフォームを使用できますが、多くのタスクでは、自動生成されたフォームが望ましいはずです。
このシステムの中核となるのは、Optionオブジェクトの概念です。これらのオブジェクトは、タイプに応じて単一の値または値のリストのいずれか、および問題のオプションに付属し、ユーザーに提示する必要のあるドキュメントを格納します。
オプションは次の引数で作成されます:
code: python
OptionType(name, value=None, documentation=None, short_explanation=None,
option_widget_keywords=None ,default=None)
short_explanation
この引数は現在、デフォルトウィジェットでは使用されていませんが、将来のバージョンで使用される予定です。ユーザーが限られた範囲のオプションから選択できるように設計されたオプションクラスも、choices引数を使用して作成できます。
すべてのオプションクラスには、クラス変数 DEFAULT および WIDGET_TO_USE もあります。これらの最初のものは、これが指定されていない場合のデフォルト値を定義します。 2つ目は、ユーザーが問題のオプションを調整できるようにするために使用されるウィジェットのクラスを指定します。
現在、次のオプションクラスが定義されています:
OptionFreeText
OptionSingleChoice
OptionMultiChoice
OptionMultiFreeList
OptionBoolean
OptionFilename
OptionDate
OptionMultiFreeText
Optionオブジェクトに格納されている値は、set(value) メソッドで設定し、get() メソッドで取得する必要があります。すべてのOptionクラスは、オーバーライド可能で、値が変更された後に呼び出されるwhen_set()メソッドも定義します。ユーザーが一連の制限された選択候補から選べるようにするオプションには、setChoices(choices) メソッドと getChoices() メソッドもあります。
オプションリストは、OptionListDisplayウィジェットを使用して表示できます。これは、value アトリビュートとしてオプションのリストを取ります。オプションを選択すると、フォームがユーザーに表示され、ドキュメント(存在する場合)が表示され、ユーザーはその値を変更できます。
オプションコレクションは、OptionListオブジェクトと一緒に収集できます。 OptionListクラスにはoptionアトリビュートがあります。これは単なるリストであり、Optionオブジェクトを追加できます。将来のバージョンでは、異なるAPIが定義される可能性があります。 OptionListオブジェクトの目的は、オブジェクトのコレクションをテストファイルに保存および復元するのに役立つことです。これらのファイルの形式は、標準のUNIXファイルと同様のカスタムテキスト形式ですが、文字列のリストを保存および復元できます(タブ文字を区切り文字として使用します)。この形式はまだ進化しており、将来のバージョンで変更される可能性があります。デフォルトとは異なる値のみが保存されます。
OptionListオブジェクトは、filename引数を使用して作成でき、write_to_file(filename=None)メソッドおよびreload_from_file(flename=None) があります。
SimpleOptionFormクラスは、これらの要素がどのように機能するかを示すために設計されたフォームです。 OptionListDisplayウィジェットは、wOptionListアトリビュートとして作成されます。
サンプルコード
次の短いデモプログラムは、呼び出しの間に指定されたオプションをファイル /tmp/testに保存します。
code: python
import npyscreen
class TestApp(npyscreen.NPSApp):
def main(self):
Options = npyscreen.OptionList()
# 便宜上、Options.optionsを書き続ける必要はありません
options = Options.options
options.append(npyscreen.OptionFreeText('FreeText', value='', documentation="This is some documentation."))
options.append(npyscreen.OptionFilename('Filename', ))
options.append(npyscreen.OptionDate('Date', ))
options.append(npyscreen.OptionMultiFreeText('Multiline Text', value=''))
options.append(npyscreen.OptionMultiFreeList('Multiline List'))
try:
Options.reload_from_file('/tmp/test')
except FileNotFoundError:
pass
F = npyscreen.Form(name = "Welcome to Npyscreen",)
ms = F.add(npyscreen.OptionListDisplay, name="Option List",
values = options,
scroll_exit=True,
max_height=None)
F.edit()
Options.write_to_file('/tmp/test')
if __name__ == "__main__":
App = TestApp()
App.run()
より複雑なフォームを書く
ターミナルアプリケーションのプログラミングの非常に典型的なスタイルは、コマンドラインを備えた画面(通常は画面の下部)と、画面の大部分を占めるある種のリストウィジェットまたはその他のディスプレイとタイトルバーを使用することでした。 上部にあり、コマンドラインの上にステータスバーがあります。 このスキームのバリエーションは、mutt、less、vim、irssiなどのアプリケーションで見られます。
これらの種類のフォームの記述を容易にするために、npyscreenは連携することを目的とした一連のクラスを提供します。
FormMuttActive、FormMuttActiveWithMenus、FormMuttActiveTraditional、FormMuttActiveTraditionalWithMenus
これらのクラスは基本的な形式を定義します。 次のクラス変数は、フォームの作成方法を正確に示します。
code: python
MAIN_WIDGET_CLASS = wgmultiline.MultiLine
MAIN_WIDGET_CLASS_START_LINE = 1
STATUS_WIDGET_CLASS = wgtextbox.Textfield
STATUS_WIDGET_X_OFFSET = 0
COMMAND_WIDGET_CLASS= wgtextbox.Textfield
COMMAND_WIDGET_NAME = None
COMMAND_WIDGET_BEGIN_ENTRY_AT = None
COMMAND_ALLOW_OVERRIDE_BEGIN_ENTRY_AT = True
DATA_CONTROLER = npysNPSFilteredData.NPSFilteredDataList
ACTION_CONTROLLER = ActionControllerSimple
デフォルトの定義では、初期化後に次のインスタンス変数を使用できるようになります。
code: python
self.wStatus1 # デフォルトでは、タイトルバー
self.wStatus2 # コマンドラインのすぐ上
self.wMain # フォームのメイン領域 デフォルトではMultiLineオブジェクト
self.wCommand # コマンドウィジェット
self.action_controller # ウィジェットではありません。 下記参照。
フォームのvalueアトリビュートは、DATA_CONTROLLER で指定されたオブジェクトのインスタンスに設定されます。
通常、アプリケーションは独自のDATA_CONTROLLERとACTION_CONTROLLERを定義する必要があります。
従来の形式と非従来の形式の違いは、従来の形式では、フォーカスは常にコマンドラインウィジェットにとどまりますが、一部のキー押下はMAIN_WIDGET_CLASSに渡されるため、ユーザーの観点からは、次のようになります。
TextCommandBox
TextCommandBoxは、ユーザーが入力した内容をaction_controllerに渡すことを除けば、通常のテキストボックスと似ています。さらに、入力されたコマンドの履歴を保持できます。詳細については、ActionControllerSimpleのドキュメントを参照してください。
TextCommandBoxTraditional
これはTextCommandBoxと同じですが、self.linked_widgetで指定されたウィジェットに特定のキーストロークを追加で渡す点が異なります。デフォルトの場合、TextCommandBoxTraditionalのハンドラーと一致しないキーストロークは、リンクされたウィジェットに渡されます。さらに、リストself.always_pass_to_linked_widget にリストされているキーストロークはすべて、リンクされたウィジェットによって処理されます。ただし、現在のコマンドラインがクラス変数 BEGINNING_OF_COMMAND_LINE_CHARS にリストされている文字で始まる場合、ユーザー入力はリンクされたウィジェットではなく、このクラスによって処理されます。
これはかなり複雑ですが、例を見るとわかりやすくなります。
デフォルトのBEGINNING_OF_COMMAND_LINE_CHARSは、カンマ(:) またはスラッシュ(/)がコマンドの開始をマークすることを指定します。その後、キーの押下はリンクされたウィジェットではなくこのウィジェットによって処理されるため、上下の矢印がコマンド履歴のナビゲートを開始します。ただし、コマンドラインが現在空の場合、これらのキーはリンクされたウィジェットの代わりにナビゲートします。
TextCommandBoxウィジェットと同様に、コマンドラインの値は親フォームのaction_controllerオブジェクトに渡されます。
ActionControllerSimple
このオブジェクトはコマンドラインを受け取り、コールバック関数を実行します。
2種類のコマンドラインを認識します。コマンドラインが変更されるたびにアクションが実行される"ライブコマンドライン"と、リターンキーが押されたときに実行される"コマンド"です。
コールバックは、add_action(ident, function, live) メソッドを使用して追加されます。 identはコマンドラインと照合される正規表現で、functionはコールバック自体です。liveはTrueまたはFalseのいずれかで、キーを押すたびにコールバックを実行するかどうかを指定しますidentが一致する場合。
正規表現 ident に一致するコマンドラインにより、次の引数を使用してコールバックが呼び出されます:
code: python
call_back(command_line, control_widget_proxy, live=True)
ここで、command_lineはコマンドラインである文字列であり、control_widget_proxyはコマンドラインウィジェットへの弱参照であり、liveは、関数が"ライブ"として呼ばれるか、戻りの結果として呼び出されるかを指定します。
メソッド create() はオーバーライドできます。オブジェクトが作成されるときに呼び出されます。デフォルトでは何もしません。 self.add_action を呼び出す場所としてこれを使用することをお勧めします。
NPSFilteredDataBase
デフォルトのNPSFilteredDataBaseクラスは、表示を管理するコードを別のオブジェクトに分離する方法を提案します。正確な方法は、アプリケーションに大きく依存します。これはこの種のアプリケーションの本質的な部分ではありませんが、(たとえば)データベースアクセスのロジックをユーザーインターフェイスのロジックから分離しておくことをお勧めします。
サンプルコード
次の例は、このモデルがどのように機能するかを示しています。アプリケーションは、検索アクションを持つActionControllerを作成します。このアクションは、フォームのparent.value(実際にはNPSFilteredDataBaseクラス)と通信するユーザー定義関数set_search()を呼び出します。次に、このクラスを使用してwMain.valuesの値を設定し、wMain.display() を呼び出して表示を更新します。
FmSearchActiveは、フォームがアクションコントローラーを使用する必要があることを指定するクラス変数を持つ、単純なFormMuttActiveTraditionalクラスです。
code: python
import npyscreen
class ActionControllerSearch(npyscreen.ActionControllerSimple):
def create(self):
self.add_action('^/.*', self.set_search, True)
def set_search(self, command_line, widget_proxy, live):
self.parent.value.set_filter(command_line1:) self.parent.wMain.values = self.parent.value.get()
self.parent.wMain.display()
class FmSearchActive(npyscreen.FormMuttActiveTraditional):
ACTION_CONTROLLER = ActionControllerSearch
class TestApp(npyscreen.NPSApp):
def main(self):
F = FmSearchActive()
F.wStatus1.value = "Status Line "
F.wStatus2.value = "Second Status Line "
F.wMain.values = F.value.get()
F.edit()
if __name__ == "__main__":
App = TestApp()
App.run()
テストコードを書く
テストの目的で、npyscreenアプリケーションのキーボード入力をスクリプト化することができます。
npyscreenモジュールは、関連する設定を含む次の辞書をエクスポートします。
code: python
TEST_SETTINGS = {
'TEST_INPUT': None,
'TEST_INPUT_LOG': [],
'CONTINUE_AFTER_TEST_INPUT': False,
'INPUT_GENERATOR': False
}
TEST_INPUTがNoneの場合、アプリケーションは正常に進行します。配列の場合、キーストロークは配列の左側から読み込まれ、キーボードから入力を取得する代わりにアプリケーションに送られます。 curses.KEYDOWN などの特殊文字を処理でき、制御文字は ^ X などの文字列で示すことができることに注意してください。
この方法でアプリケーションに入力されたキー押下は、TEST_INPUT_LOG に自動的に追加されるため、入力の処理時にエラーが発生した場所を確認できます。
CONTINUE_AFTER_TEST_INPUT が True の場合、自動入力が指定された後、TEST_INPUT は None に設定され、アプリケーションは通常どおり続行されます。 False の場合、代わりに例外ExhaustedTestInput が発生します。これにより、ユニットテストでアプリケーションの状態をテストできます。
INPUT_GENERATOR は反復可能なオブジェクトに設定できます。各キー押下は、next(INPUT_GENERATOR) を呼び出すことによって読み取られます。選択した反復可能オブジェクトがスレッドセーフである場合、これにより、1つのスレッドを使用してテスト入力を簡単にフィードできます。これは、TEST_INPUT よりも優先して使用できます。バージョン4.9の新機能であり、ユーザーの要求に応じて追加されます。
コンビニエンス関数
バージョン4.8.5の新機能
npyscreen.add_test_input_from_iterable(iterable)
iterableの各項目をTEST_SETTINGS [‘TEST_INPUT’]に追加します。
npyscreen.add_test_input_ch(ch)
ch を TEST_SETTINGS [‘TEST_INPUT’] に追加します。
ユニットテストを書くためのフォークの防止
基盤となるcursesモジュールでのメモリリークを回避するために、npyscreenライブラリはフォークされたプロセスでアプリケーションコードを実行することを選択することがあります。テストの目的では、これは通常望ましくありません。テストの目的で、アプリケーションのrun()メソッドにfork=False を渡すことをお勧めします。
以下は簡単な例です。
code: python
import curses
import npyscreen
class TestForm(npyscreen.Form):
def create(self):
self.myTitleText = self.add(npyscreen.TitleText, name="Events (Form Controlled):", editable=True)
class TestApp(npyscreen.StandardApp):
def onStart(self):
self.addForm("MAIN", TestForm)
if __name__ == '__main__':
A = TestApp()
A.run(fork=False)
# "これはテストです" は、ユーザーが入力したかのように、最初のウィジェットに表示されます。
アプリケーション例:簡単なアドレス帳
アドレス帳アプリケーションにはデータベースが必要です。 便宜上、sqliteを使用します。
code: python
class AddressDatabase(object):
def __init__(self, filename="example-addressbook.db"):
self.dbfilename = filename
db = sqlite3.connect(self.dbfilename)
c = db.cursor()
c.execute(
"CREATE TABLE IF NOT EXISTS records\
( record_internal_id INTEGER PRIMARY KEY, \
last_name TEXT, \
other_names TEXT, \
email_address TEXT \
)" \
)
db.commit()
c.close()
def add_record(self, last_name = '', other_names='', email_address=''):
db = sqlite3.connect(self.dbfilename)
c = db.cursor()
c.execute('INSERT INTO records(last_name, other_names, email_address) \
VALUES(?,?,?)', (last_name, other_names, email_address))
db.commit()
c.close()
def update_record(self, record_id, last_name = '', other_names='', email_address=''):
db = sqlite3.connect(self.dbfilename)
c = db.cursor()
c.execute('UPDATE records set last_name=?, other_names=?, email_address=? \
WHERE record_internal_id=?',
(last_name, other_names, email_address, record_id))
db.commit()
c.close()
def delete_record(self, record_id):
db = sqlite3.connect(self.dbfilename)
c = db.cursor()
c.execute('DELETE FROM records where record_internal_id=?', (record_id,))
db.commit()
c.close()
def list_all_records(self, ):
db = sqlite3.connect(self.dbfilename)
c = db.cursor()
c.execute('SELECT * from records')
records = c.fetchall()
c.close()
return records
def get_record(self, record_id):
db = sqlite3.connect(self.dbfilename)
c = db.cursor()
c.execute('SELECT * from records WHERE record_internal_id=?', (record_id,))
records = c.fetchall()
c.close()
アプリケーションのメイン画面は名前のリストになります。 ユーザーが名前を選択したら、それを編集します。 MultiLineActionをサブクラス化し、表示値をオーバーライドして、各レコードの表示方法を変更します。 また、必要に応じて、actionHighlightedメソッドをオーバーライドして編集フォームに切り替えます。 最後に、2つの新しいキー押下を追加します。1つはレコードの追加、もう1つはレコードの削除です。 EDITRECORDFMに切り替える前に、新しいフォームを作成する場合はその値をNoneに設定するか、編集するレコードの値に設定します。
code: python
class RecordList(npyscreen.MultiLineAction):
def __init__(self, *args, **keywords):
super(RecordList, self).__init__(*args, **keywords)
self.add_handlers({
"^A": self.when_add_record,
"^D": self.when_delete_record
})
def display_value(self, vl):
return "%s, %s" % (vl1, vl2) def actionHighlighted(self, act_on_this, keypress):
self.parent.parentApp.getForm('EDITRECORDFM').value =act_on_this0 self.parent.parentApp.switchForm('EDITRECORDFM')
def when_add_record(self, *args, **keywords):
self.parent.parentApp.getForm('EDITRECORDFM').value = None
self.parent.parentApp.switchForm('EDITRECORDFM')
def when_delete_record(self, *args, **keywords):
self.parent.update_list()
レコードリストを表示する実際のフォームは、FormMuttサブクラスになります。 RecordListウィジェットを使用するようにMAIN_WIDGET_CLASSクラス変数を変更し、フォームがユーザーに提示されるたびにレコードのリストが更新されるようにします。
code: python
class RecordListDisplay(npyscreen.FormMutt):
MAIN_WIDGET_CLASS = RecordList
def beforeEditing(self):
self.update_list()
def update_list(self):
self.wMain.values = self.parentApp.myDatabase.list_all_records()
self.wMain.display()
各レコードを編集するためのフォームは、ActionFormの例になります。 レコードは、ユーザーが[OK]ボタンを選択した場合にのみ変更されます。 フォームがユーザーに表示される前に、個々のウィジェットのそれぞれの値がデータベースレコードと一致するように更新されるか、新しいレコードを作成する場合はクリアされます。
code: python
class EditRecord(npyscreen.ActionForm):
def create(self):
self.value = None
self.wgLastName = self.add(npyscreen.TitleText, name = "Last Name:",)
self.wgOtherNames = self.add(npyscreen.TitleText, name = "Other Names:")
self.wgEmail = self.add(npyscreen.TitleText, name = "Email:")
def beforeEditing(self):
if self.value:
record = self.parentApp.myDatabase.get_record(self.value)
self.name = "Record id : %s" % record0 self.wgLastName.value = record1 self.wgOtherNames.value = record2 self.wgEmail.value = record3 else:
self.name = "New Record"
self.record_id = ''
self.wgLastName.value = ''
self.wgOtherNames.value = ''
self.wgEmail.value = ''
def on_ok(self):
if self.record_id: # 既存のレコードを編集します
self.parentApp.myDatabase.update_record(self.record_id,
last_name=self.wgLastName.value,
other_names = self.wgOtherNames.value,
email_address = self.wgEmail.value,
)
else: # 新しいレコードを追加します
self.parentApp.myDatabase.add_record(last_name=self.wgLastName.value,
other_names = self.wgOtherNames.value,
email_address = self.wgEmail.value,
)
self.parentApp.switchFormPrevious()
def on_cancel(self):
self.parentApp.switchFormPrevious()
最後に、2つのフォームとデータベースを管理するアプリケーションオブジェクトが必要です。
code: python
class AddressBookApplication(npyscreen.NPSAppManaged):
def onStart(self):
self.myDatabase = AddressDatabase()
self.addForm("MAIN", RecordListDisplay)
self.addForm("EDITRECORDFM", EditRecord)
if __name__ == '__main__':
myApp = AddressBookApplication()
myApp.run()
参考